home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / init.c < prev    next >
C/C++ Source or Header  |  1996-07-10  |  142KB  |  4,362 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: init.c,v 4.369 1996/07/10 23:04:18 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      init.c
  44.      Routines for pine start up and initialization
  45.        init_vars
  46.        free_vars
  47.        init_username
  48.        init_hostname
  49.        read_pinerc
  50.        write_pinerc
  51.        init_mail_dir
  52.  
  53.        sent-mail expiration
  54.  
  55.        open debug files 
  56.  
  57.       - open and set up the debug files
  58.       - fetch username, password, and home directory
  59.       - get host and domain name
  60.       - read and write the users .pinerc config file
  61.       - create the "mail" subdirectory
  62.       - expire old sent-mail
  63.  
  64.   ====*/
  65.  
  66.  
  67. #include "headers.h"
  68.  
  69.  
  70. typedef enum {ParseLocal, ParseGlobal, ParseFixed} ParsePinerc;
  71. typedef enum {Sapling, Seedling, Seasoned} FeatureLevel;
  72.  
  73.  
  74. /*
  75.  * Internal prototypes
  76.  */
  77. void init_error PROTO((struct pine *, char *));
  78. void read_pinerc PROTO((char *, struct variable *, ParsePinerc));
  79. int  compare_sm_files PROTO((const QSType *, const QSType *));
  80. /* AIX gives warning here 'cause it can't quite cope with enums */
  81. void process_init_cmds PROTO((struct pine *, char **));
  82. void process_feature_list PROTO((struct pine *, char **, int , int, int));
  83. int  decode_sort PROTO((struct pine *, char *));
  84. void display_init_err PROTO((char *, int));
  85.  
  86.  
  87. #if    defined(DOS_EXTRA) && !(defined(WIN32) || defined (_WINDOWS))
  88. #define    CONF_TXT_T    char __based(__segname("_CNFT"))
  89. #else
  90. #define    CONF_TXT_T    char
  91. #endif
  92.  
  93. /*------------------------------------
  94. Some definitions to keep the static "variable" array below 
  95. a bit more readable...
  96.   ----*/
  97. /* be careful changing these PINERC_COMMENTs */
  98. CONF_TXT_T cf_text_comment1[] = "# Updated by Pine(tm) ";
  99. CONF_TXT_T cf_text_comment2[] = ", copyright 1989-1996 University of Washington.\n";
  100. CONF_TXT_T cf_text_comment3[] = "#\n# Pine configuration file -- customize as needed.\n#\n# This file sets the configuration options used by Pine and PC-Pine.  If you\n# are using Pine on a Unix system, there may be a system-wide configuration\n# file which sets the defaults for these variables.  There are comments in\n# this file to explain each variable, but if you have questions about\n# specific\
  101.  settings see the section on configuration options in the Pine\n# notes.  On Unix, run pine -conf to see how system defaults have been set.\n# For variables that accept multiple values, list elements are separated\n# by commas.  A line beginning with a space or tab is considered to be a\n# continuation of the previous line.  For a variable to be unset its value\n# must be blank.  To set a variable to the empty string its value should\n# \
  102. be \"\".  You can override system defaults by setting a variable to the\n# empty string.  Switch variables are set to either \"yes\" or \"no\", and\n# default to \"no\".\n# Lines beginning with \"#\" are comments, and ignored by Pine.\n";
  103.  
  104.  
  105. CONF_TXT_T cf_text_personal_name[] =    "Over-rides your full name from Unix password file. Required for PC-Pine.";
  106.  
  107. CONF_TXT_T cf_text_user_id[] =        "Your login/e-mail user name";
  108.  
  109. CONF_TXT_T cf_text_user_domain[] =        "Sets domain part of From: and local addresses in outgoing mail.";
  110.  
  111. CONF_TXT_T cf_text_smtp_server[] =        "List of SMTP servers for sending mail. If blank: Unix Pine uses sendmail.";
  112.  
  113. CONF_TXT_T cf_text_nntp_server[] =        "NNTP server for posting news. Also sets news-collections for news reading.";
  114.  
  115. CONF_TXT_T cf_text_inbox_path[] =        "Path of (local or remote) INBOX, e.g. ={mail.somewhere.edu}inbox\n# Normal Unix default is the local INBOX (usually /usr/spool/mail/$USER).";
  116.  
  117. CONF_TXT_T cf_text_incoming_folders[] =    "List of incoming msg folders besides INBOX, e.g. ={host2}inbox, {host3}inbox\n# Syntax: optnl-label {optnl-imap-host-name}folder-path";
  118.  
  119. CONF_TXT_T cf_text_folder_collections[] =    "List of directories where saved-message folders may be. First one is\n# the default for Saves. Example: Main {host1}mail/[], Desktop mail\\[]\n# Syntax: optnl-label {optnl-imap-hostname}optnl-directory-path[]";
  120.  
  121. CONF_TXT_T cf_text_news_collections[] =    "List, only needed if nntp-server not set, or news is on a different host\n# than used for NNTP posting. Examples: News *[] or News *{host3/nntp}[]\n# Syntax: optnl-label *{news-host/protocol}[]";
  122.  
  123. CONF_TXT_T cf_text_pruned_folders[] =    "List of context and folder pairs, delimited by a space, to be offered for\n# pruning each month.  For example: {host1}mail/[] mumble";
  124.  
  125. CONF_TXT_T cf_text_default_fcc[] =        "Over-rides default path for sent-mail folder, e.g. =old-mail (using first\n# folder collection dir) or ={host2}sent-mail or =\"\" (to suppress saving).\n# Default: sent-mail (Unix) or SENTMAIL.MTX (PC) in default folder collection.";
  126.  
  127. CONF_TXT_T cf_text_default_saved[] =    "Over-rides default path for saved-msg folder, e.g. =saved-messages (using first\n# folder collection dir) or ={host2}saved-mail or =\"\" (to suppress saving).\n# Default: saved-messages (Unix) or SAVEMAIL.MTX (PC) in default folder collection.";
  128.  
  129. CONF_TXT_T cf_text_postponed_folder[] =    "Over-rides default path for postponed messages folder, e.g. =pm (which uses\n# first folder collection dir) or ={host4}pm (using home dir on host4).\n# Default: postponed-msgs (Unix) or POSTPOND.MTX (PC) in default fldr coltn.";
  130.  
  131. CONF_TXT_T cf_text_mail_directory[] =    "Pine compares this value with the first folder collection directory.\n# If they match (or no folder collections are defined), and the directory\n# does not exist, Pine will create and use it. Default: ~/mail";
  132.  
  133. CONF_TXT_T cf_text_read_message_folder[] =    "If set, specifies where already-read messages will be moved upon quitting.";
  134.  
  135. CONF_TXT_T cf_text_signature_file[] =    "Over-rides default path for signature file. Default is ~/.signature";
  136.  
  137. CONF_TXT_T cf_text_global_address_book[] =    "List of file or path names for global/shared addressbook(s).\n# Default: none\n# Syntax: optnl-label path-name";
  138.  
  139. CONF_TXT_T cf_text_address_book[] =    "List of file or path names for personal addressbook(s).\n# Default: ~/.addressbook (Unix) or \\PINE\\ADDRBOOK (PC)\n# Syntax: optnl-label path-name";
  140.  
  141. CONF_TXT_T cf_text_feature_list[] =    "List of features; see Pine's Setup/options menu for the current set.\n# e.g. feature-list= select-without-confirm, signature-at-bottom\n# Default condition for all of the features is no-.";
  142.  
  143. CONF_TXT_T cf_text_initial_keystroke_list[] =    "Pine executes these keys upon startup (e.g. to view msg 13: i,j,1,3,CR,v)";
  144.  
  145. CONF_TXT_T cf_text_default_composer_hdrs[] =    "Only show these headers (by default) when composing messages";
  146.  
  147. CONF_TXT_T cf_text_customized_hdrs[] =    "Add these customized headers (and possible default values) when composing";
  148.  
  149. CONF_TXT_T cf_text_view_headers[] =    "When viewing messages, include this list of headers";
  150.  
  151. CONF_TXT_T cf_text_save_msg_name_rule[] =    "Determines default folder name for Saves...\n# Choices: default-folder, by-sender, by-from, by-recipient, last-folder-used.\n# Default: \"default-folder\", i.e. \"saved-messages\" (Unix) or \"SAVEMAIL\" (PC).";
  152.  
  153. CONF_TXT_T cf_text_fcc_name_rule[] =    "Determines default name for Fcc...\n# Choices: default-fcc, by-recipient, last-fcc-used.\n# Default: \"default-fcc\" (see also \"default-fcc=\" variable.)";
  154.  
  155. CONF_TXT_T cf_text_sort_key[] =        "Sets presentation order of messages in Index. Choices:\n# subject, from, arrival, date, size. Default: \"arrival\".";
  156.  
  157. CONF_TXT_T cf_text_addrbook_sort_rule[] =    "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\".";
  158.  
  159. CONF_TXT_T cf_text_character_set[] =    "Reflects capabilities of the display you have. Default: US-ASCII.\n# Typical alternatives include ISO-8859-x, (x is a number between 1 and 9).";
  160.  
  161. CONF_TXT_T cf_text_editor[] =        "Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature.";
  162.  
  163. CONF_TXT_T cf_text_speller[] =        "Specifies the program invoked by ^T in the Composer.";
  164.  
  165. CONF_TXT_T cf_text_fillcol[] =        "Specifies the column of the screen where the composer should wrap.";
  166.  
  167. CONF_TXT_T cf_text_replystr[] =        "Specifies the string to insert when replying to  message.";
  168.  
  169. CONF_TXT_T cf_text_emptyhdr[] =        "Specifies the string to use when sending a  message with no to or cc.";
  170.  
  171. CONF_TXT_T cf_text_image_viewer[] =    "Program to view images (e.g. GIF or TIFF attachments).";
  172.  
  173. CONF_TXT_T cf_text_use_only_domain_name[] = "If \"user-domain\" not set, strips hostname in FROM address. (Unix only)";
  174.  
  175. CONF_TXT_T cf_text_printer[] =        "Your default printer selection";
  176.  
  177. CONF_TXT_T cf_text_personal_print_command[] =    "List of special print commands";
  178.  
  179. CONF_TXT_T cf_text_personal_print_cat[] =    "Which category default print command is in";
  180.  
  181. CONF_TXT_T cf_text_standard_printer[] =    "The system wide standard printers";
  182.  
  183. CONF_TXT_T cf_text_last_time_prune_quest[] =    "Set by Pine; controls beginning-of-month sent-mail pruning.";
  184.  
  185. CONF_TXT_T cf_text_last_version_used[] =    "Set by Pine; controls display of \"new version\" message.";
  186.  
  187. CONF_TXT_T cf_text_bugs_fullname[] =    "Full name for bug report address used by \"Report Bug\" command.\n# Default: Pine Developers";
  188.  
  189. CONF_TXT_T cf_text_bugs_address[] =    "Email address used to send bug reports.\n# Default: pine-bugs@cac.washington.edu";
  190.  
  191. CONF_TXT_T cf_text_bugs_extras[] =        "Program/Script used by \"Report Bug\" command. No default.";
  192.  
  193. CONF_TXT_T cf_text_suggest_fullname[] =    "Full name for suggestion address used by \"Report Bug\" command.\n# Default: Pine Developers";
  194.  
  195. CONF_TXT_T cf_text_suggest_address[] =    "Email address used to send suggestions.\n# Default: pine-suggestions@cac.washington.edu";
  196.  
  197. CONF_TXT_T cf_text_local_fullname[] =    "Full name for \"local support\" address used by \"Report Bug\" command.\n# Default: Local Support";
  198.  
  199. CONF_TXT_T cf_text_local_address[] =    "Email address used to send to \"local support\".\n# Default: postmaster";
  200.  
  201. CONF_TXT_T cf_text_forced_abook[] =    "Force these address book entries into all writable personal address books.\n# Syntax is   forced-abook-entry=nickname|fullname|address\n# This is a comma-separated list of entries, each with syntax above.\n# Existing entries with same nickname are not replaced.\n# Example: help|Help Desk|help@ourdomain.com";
  202.  
  203. CONF_TXT_T cf_text_kblock_passwd[] =    "This is a number between 1 and 5.  It is the number of times a user will\n# have to enter a password when they run the keyboard lock command in the\n# main menu.  Default is 1.";
  204.  
  205. CONF_TXT_T cf_text_sendmail_path[] =    "This names the path to an alternative program, and any necessary arguments,\n# to be used in posting mail messages.  Example:\n#                    /usr/lib/sendmail -oem -t -oi\n# or,\n#                    /usr/local/bin/sendit.sh\n# The latter a script found in Pine distribution's contrib/util directory.\n# NOTE: The program MUST read the message to be posted on standard input,\n#       AND operate in the style of sendmail's \"-t\" option.";
  206.  
  207. CONF_TXT_T cf_text_oper_dir[] =    "This names the root of the tree to which the user is restricted when reading\n# and writing folders and files.  For example, on Unix ~/work confines the\n# user to the subtree beginning with their work subdirectory.\n# (Note: this alone is not sufficient for preventing access.  You will also\n# need to restrict shell access and so on, see Pine Technical Notes.)\n# Default: not set (so no restriction)";
  208.  
  209. CONF_TXT_T cf_text_in_fltr[] =         "This variable takes a list of programs that message text is piped into\n# after MIME decoding, prior to display.";
  210.  
  211. CONF_TXT_T cf_text_out_fltr[] =        "This defines a program that message text is piped into before MIME\n# encoding, prior to sending";
  212.  
  213. CONF_TXT_T cf_text_alt_addrs[] =        "A list of alternate addresses the user is known by";
  214.  
  215. CONF_TXT_T cf_text_abook_formats[] =    "This is a list of formats for address books.  Each entry in the list is made\n# up of space-delimited tokens telling which fields are displayed and in\n# which order.  See help text";
  216.  
  217. CONF_TXT_T cf_text_index_format[] =    "This gives a format for displaying the index.  It is made\n# up of space-delimited tokens telling which fields are displayed and in\n# which order.  See help text";
  218.  
  219. CONF_TXT_T cf_text_overlap[] =        "The number of lines of overlap when scrolling through message text";
  220.  
  221. CONF_TXT_T cf_text_margin[] =        "Number of lines from top and bottom of screen where single\n# line scrolling occurs.";
  222.  
  223. CONF_TXT_T cf_text_stat_msg_delay[] =    "The number of seconds to sleep after writing a status message";
  224.  
  225. CONF_TXT_T cf_text_mailcheck[] =        "The approximate number of seconds between checks for new mail";
  226.  
  227. CONF_TXT_T cf_text_news_active[] =        "Path and filename of news configation's active file.\n# The default is typically \"/usr/lib/news/active\".";
  228.  
  229. CONF_TXT_T cf_text_news_spooldir[] =    "Directory containing system's news data.\n# The default is typically \"/usr/spool/news\"";
  230.  
  231. CONF_TXT_T cf_text_upload_cmd[] =        "Path and filename of the program used to upload text from your terminal\n# emulator's into Pine's composer.";
  232.  
  233. CONF_TXT_T cf_text_upload_prefix[] =    "Text sent to terminal emulator prior to invoking the program defined by\n# the upload-command variable.\n# Note: _FILE_ will be replaced with the temporary file used in the upload.";
  234.  
  235. CONF_TXT_T cf_text_download_cmd[] =    "Path and filename of the program used to download text via your terminal\n# emulator from Pine's export and save commands.";
  236.  
  237. CONF_TXT_T cf_text_download_prefix[] =    "Text sent to terminal emulator prior to invoking the program defined by\n# the download-command variable.\n# Note: _FILE_ will be replaced with the temporary file used in the downlaod.";
  238.  
  239. CONF_TXT_T cf_text_goto_default[] =    "Sets the default folder and collectionoffered at the Goto Command's prompt.";
  240.  
  241. CONF_TXT_T cf_text_mailcap_path[] =    "Sets the search path for the mailcap cofiguration file.\n# NOTE: colon delimited under UNIX, semi-colon delimited under DOS/Windows/OS2.";
  242.  
  243. CONF_TXT_T cf_text_mimetype_path[] =    "Sets the search path for the mimetypes cofiguration file.\n# NOTE: colon delimited under UNIX, semi-colon delimited under DOS/Windows/OS2.";
  244.  
  245. CONF_TXT_T cf_text_tcp_open_timeo[] =    "Sets the time in seconds that Pine will attempt to open a network\n# connection.  The default is 30, the minimum is 5, and the maximum is\n# system defined (typically 75).";
  246.  
  247. CONF_TXT_T cf_text_rsh_open_timeo[] =    "Sets the time in seconds that Pine will attempt to open a UNIX remote\n# shell connection.  The default is 15, min is 5, and max is unlimited.\n# Zero disables rsh altogether.";
  248.  
  249. CONF_TXT_T cf_text_version_threshold[] = "Sets the version number Pine will use as a threshold for offering\n# its new version message on startup.";
  250.  
  251. CONF_TXT_T cf_text_archived_folders[] =    "List of folder pairs; the first indicates a folder to archive, and the\n# second indicates the folder read messages in the first should\n# be moved to.";
  252.  
  253. CONF_TXT_T cf_text_elm_style_save[] =    "Elm-style-save is obsolete, use saved-msg-name-rule";
  254.  
  255. CONF_TXT_T cf_text_header_in_reply[] =    "Header-in-reply is obsolete, use include-header-in-reply in feature-list";
  256.  
  257. CONF_TXT_T cf_text_feature_level[] =    "Feature-level is obsolete, use feature-list";
  258.  
  259. CONF_TXT_T cf_text_old_style_reply[] =    "Old-style-reply is obsolete, use signature-at-bottom in feature-list";
  260.  
  261. CONF_TXT_T cf_text_compose_mime[] =    "Compose-mime is obsolete";
  262.  
  263. CONF_TXT_T cf_text_show_all_characters[] =    "Show-all-characters is obsolete";
  264.  
  265. CONF_TXT_T cf_text_save_by_sender[] =    "Save-by-sender is obsolete, use saved-msg-name-rule";
  266.  
  267. CONF_TXT_T cf_text_nntp_new_group_time[] =    "Last time NNTP server was checked for newly created news groups";
  268.  
  269. CONF_TXT_T cf_text_folder_extension[] =    "Extension used for local folder names (\".MTX\" by default).";
  270.  
  271. CONF_TXT_T cf_text_normal_foreground_color[] =    "Choose: black,blue,green,cyan,red,magenta,yellow,or white (CAPS=BLINKING).";
  272.  
  273. CONF_TXT_T cf_text_window_position[] =    "Window position in the format: CxR+X+Y\n# Where C and R are the window size in characters and X and Y are the\n# screen position of the top left corner of the window.";
  274.  
  275. CONF_TXT_T cf_text_newsrc_path[] =        "Full path and name of NEWSRC file";
  276.  
  277.  
  278. /* these sort of divide up the pinerc file into categories */
  279. char    cf_before_personal_name[] =    "########################### Essential Parameters ###########################";
  280. char    cf_before_incoming_folders[] =    "###################### Collections, Folders, and Files #####################";
  281. char    cf_before_feature_list[] =    "############################### Preferences ################################";
  282. char    cf_before_printer[] =    "########## Set within or by Pine: No need to edit below this line ##########";
  283.  
  284.  
  285. /* these are used to report folder directory creation problems */
  286. CONF_TXT_T init_md_exists[] =    "The \"%s\" subdirectory already exists, but it is not writable by Pine so Pine cannot run.  Please correct the permissions and restart Pine.";
  287.  
  288. CONF_TXT_T init_md_file[] =    "Pine requires a directory called \"%s\" and usualy creates it.  You already have a regular file by that name which means Pine cannot create the directory.  Please move or remove it and start Pine again.";
  289.  
  290. CONF_TXT_T init_md_create[] =    "Creating subdirectory \"%s\" where Pine will store its mail folders.";
  291.  
  292.  
  293. static int    copyright_line_is_there,
  294.         trademark_lines_are_there;
  295.  
  296.  
  297. /*----------------------------------------------------------------------
  298. These are the variables that control a number of pine functions.  They
  299. come out of the .pinerc and the /usr/local/lib/pine.conf files.  Some can
  300. be set by the user while in Pine.  Eventually all the local ones should
  301. be so and maybe the global ones too.
  302.  
  303. Each variable can have a command-line, user, global, and current value.
  304. All of these values are malloc'd.  The user value is the one read out of
  305. the user's .pinerc, the global value is the one from the system pine
  306. configuration file.  There are often defaults for the global values, set
  307. at the start of init_vars().  Perhaps someday there will be group values.
  308. The current value is the one that is actually in use.
  309.   ----*/
  310. /* name::is_obsolete::is_used::been_written::is_user::is_global::is_list::
  311.    is_fixed::description */
  312. static struct variable variables[] = {
  313. {"personal-name",              0, 1, 0, 1, 1, 0, 0, cf_text_personal_name},
  314. #if defined(DOS) || defined(OS2)
  315.                         /* Have to have this on DOS, PC's, Macs, etc... */
  316. {"user-id",                    0, 1, 0, 1, 0, 0, 0,
  317. #else            /* Don't allow on UNIX machines for some security */
  318. {"user-id",                    0, 0, 0, 1, 0, 0, 0,
  319. #endif
  320.     cf_text_user_id},
  321. {"user-domain",                0, 1, 0, 1, 1, 0, 0, cf_text_user_domain},
  322. {"smtp-server",                0, 1, 0, 1, 1, 1, 0, cf_text_smtp_server},
  323. {"nntp-server",                0, 1, 0, 1, 1, 1, 0, cf_text_nntp_server},
  324. {"inbox-path",                 0, 1, 0, 1, 1, 0, 0, cf_text_inbox_path},
  325. {"incoming-folders",           0, 1, 0, 1, 1, 1, 0, cf_text_incoming_folders},
  326. {"folder-collections",         0, 1, 0, 1, 1, 1, 0,
  327.                            cf_text_folder_collections},
  328. {"news-collections",           0, 1, 0, 1, 1, 1, 0, cf_text_news_collections},
  329. {"incoming-archive-folders",   0, 1, 0, 1, 1, 1, 0, cf_text_archived_folders},
  330. {"pruned-folders",           0, 1, 0, 1, 1, 1, 0, cf_text_pruned_folders},
  331. {"default-fcc",                0, 1, 0, 1, 1, 0, 0, cf_text_default_fcc},
  332. {"default-saved-msg-folder",   0, 1, 0, 1, 1, 0, 0, cf_text_default_saved},
  333. {"postponed-folder",           0, 1, 0, 1, 1, 0, 0, cf_text_postponed_folder},
  334. {"mail-directory",             0, 1, 0, 0, 1, 0, 0, cf_text_mail_directory},
  335. {"read-message-folder",        0, 1, 0, 1, 1, 0, 0,
  336.                           cf_text_read_message_folder},
  337. {"signature-file",             0, 1, 0, 1, 1, 0, 0, cf_text_signature_file},
  338. {"global-address-book",        0, 1, 0, 1, 1, 1, 0,
  339.                           cf_text_global_address_book},
  340. {"address-book",               0, 1, 0, 1, 1, 1, 0, cf_text_address_book},
  341. {"feature-list",               0, 1, 0, 1, 1, 1, 0, cf_text_feature_list},
  342. {"initial-keystroke-list",     0, 1, 0, 1, 1, 1, 0,
  343.                            cf_text_initial_keystroke_list},
  344. {"default-composer-hdrs",      0, 1, 0, 1, 1, 1, 0,
  345.                             cf_text_default_composer_hdrs},
  346. {"customized-hdrs",            0, 1, 0, 1, 1, 1, 0, cf_text_customized_hdrs},
  347. {"viewer-hdrs",                0, 1, 0, 1, 1, 1, 0, cf_text_view_headers},
  348. {"saved-msg-name-rule",        0, 1, 0, 1, 1, 0, 0,
  349.                            cf_text_save_msg_name_rule},
  350. {"fcc-name-rule",              0, 1, 0, 1, 1, 0, 0, cf_text_fcc_name_rule},
  351. {"sort-key",                   0, 1, 0, 1, 1, 0, 0, cf_text_sort_key},
  352. {"addrbook-sort-rule",         0, 1, 0, 1, 1, 0, 0,
  353.                            cf_text_addrbook_sort_rule},
  354. {"goto-default-rule",           0, 1, 0, 1, 1, 0, 0, cf_text_goto_default},
  355. {"character-set",              0, 1, 0, 1, 1, 0, 0, cf_text_character_set},
  356. {"editor",                     0, 1, 0, 1, 1, 0, 0, cf_text_editor},
  357. {"speller",                    0, 1, 0, 1, 1, 0, 0, cf_text_speller},
  358. {"composer-wrap-column",       0, 1, 0, 1, 1, 0, 0, cf_text_fillcol},
  359. {"reply-indent-string",        0, 1, 0, 1, 1, 0, 0, cf_text_replystr},
  360. {"empty-header-message",       0, 1, 0, 1, 1, 0, 0, cf_text_emptyhdr},
  361. {"image-viewer",               0, 1, 0, 1, 1, 0, 0, cf_text_image_viewer},
  362. {"use-only-domain-name",       0, 1, 0, 1, 1, 0, 0,
  363.                          cf_text_use_only_domain_name},
  364. {"printer",                    0, 1, 0, 1, 1, 0, 0, cf_text_printer},
  365. {"personal-print-command",     0, 1, 0, 1, 1, 1, 0,
  366.                            cf_text_personal_print_command},
  367. {"personal-print-category",    0, 1, 0, 1, 0, 0, 0,
  368.                            cf_text_personal_print_cat},
  369. {"standard-printer",           0, 1, 0, 0, 1, 1, 0, cf_text_standard_printer},
  370. {"last-time-prune-questioned" ,0, 1, 0, 1, 0, 0, 0,
  371.                        cf_text_last_time_prune_quest},
  372. {"last-version-used",          0, 1, 0, 1, 0, 0, 0, cf_text_last_version_used},
  373. {"bugs-fullname",              0, 1, 0, 0, 1, 0, 0, cf_text_bugs_fullname},
  374. {"bugs-address",               0, 1, 0, 0, 1, 0, 0, cf_text_bugs_address},
  375. {"bugs-additional-data",       0, 1, 0, 0, 1, 0, 0, cf_text_bugs_extras},
  376. {"suggest-fullname",           0, 1, 0, 0, 1, 0, 0, cf_text_suggest_fullname},
  377. {"suggest-address",            0, 1, 0, 0, 1, 0, 0, cf_text_suggest_address},
  378. {"local-fullname",             0, 1, 0, 0, 1, 0, 0, cf_text_local_fullname},
  379. {"local-address",              0, 1, 0, 0, 1, 0, 0, cf_text_local_address},
  380. {"forced-abook-entry",         0, 1, 0, 0, 1, 1, 0, cf_text_forced_abook},
  381. {"kblock-passwd-count",        0, 1, 0, 0, 1, 0, 0, cf_text_kblock_passwd},
  382. {"sendmail-path",           0, 1, 0, 1, 1, 0, 0, cf_text_sendmail_path},
  383. {"operating-dir",           0, 1, 0, 1, 1, 0, 0, cf_text_oper_dir},
  384. {"display-filters",           0, 1, 0, 1, 1, 1, 0, cf_text_in_fltr},
  385. {"sending-filters",           0, 1, 0, 1, 1, 1, 0, cf_text_out_fltr},
  386. {"alt-addresses",              0, 1, 0, 1, 1, 1, 0, cf_text_alt_addrs},
  387. {"addressbook-formats",        0, 1, 0, 1, 1, 1, 0, cf_text_abook_formats},
  388. {"index-format",               0, 1, 0, 1, 1, 0, 0, cf_text_index_format},
  389. {"viewer-overlap",             0, 1, 0, 1, 1, 0, 0, cf_text_overlap},
  390. {"scroll-margin",              0, 1, 0, 1, 1, 0, 0, cf_text_margin},
  391. {"status-message-delay",       0, 1, 0, 1, 1, 0, 0, cf_text_stat_msg_delay},
  392. {"mail-check-interval",        0, 1, 0, 1, 1, 0, 0, cf_text_mailcheck},
  393. {"newsrc-path",               0, 1, 0, 1, 1, 0, 0, cf_text_newsrc_path},
  394. {"news-active-file-path",      0, 1, 0, 1, 1, 0, 0, cf_text_news_active},
  395. {"news-spool-directory",       0, 1, 0, 1, 1, 0, 0, cf_text_news_spooldir},
  396. {"upload-command",           0, 1, 0, 1, 1, 0, 0, cf_text_upload_cmd},
  397. {"upload-command-prefix",      0, 1, 0, 1, 1, 0, 0, cf_text_upload_prefix},
  398. {"download-command",           0, 1, 0, 1, 1, 0, 0, cf_text_download_cmd},
  399. {"download-command-prefix",    0, 1, 0, 1, 1, 0, 0, cf_text_download_prefix},
  400. {"mailcap-search-path",           0, 1, 0, 1, 1, 0, 0, cf_text_mailcap_path},
  401. {"mimetype-search-path",       0, 1, 0, 1, 1, 0, 0, cf_text_mimetype_path},
  402. {"tcp-open-timeout",           0, 1, 0, 1, 1, 0, 0, cf_text_tcp_open_timeo},
  403. {"rsh-open-timeout",           0, 1, 0, 1, 1, 0, 0, cf_text_rsh_open_timeo},
  404. {"new-version-threshold",      0, 1, 0, 1, 1, 0, 0, cf_text_version_threshold},
  405.  
  406. #ifdef NEWBB
  407. {"nntp-new-group-time",        0, 1, 0, 1, 0, 0, 0,
  408.                           cf_text_nntp_new_group_time},
  409. #endif
  410. /* OBSOLETE VARS */
  411. {"elm-style-save",             1, 1, 0, 1, 1, 0, 0, cf_text_elm_style_save},
  412. {"header-in-reply",            1, 1, 0, 1, 1, 0, 0, cf_text_header_in_reply},
  413. {"feature-level",              1, 1, 0, 1, 1, 0, 0, cf_text_feature_level},
  414. {"old-style-reply",            1, 1, 0, 1, 1, 0, 0, cf_text_old_style_reply},
  415. {"compose-mime",               1, 1, 0, 0, 1, 0, 0, cf_text_compose_mime},
  416. {"show-all-characters",        1, 1, 0, 1, 1, 0, 0,
  417.                           cf_text_show_all_characters},
  418. {"save-by-sender",             1, 1, 0, 1, 1, 0, 0, cf_text_save_by_sender},
  419. #if defined(DOS) || defined(OS2)
  420. {"folder-extension",           0, 1, 0, 1, 1, 0, 0, cf_text_folder_extension},
  421. {"normal-foreground-color",    0, 1, 0, 1, 1, 0, 0,
  422.                         cf_text_normal_foreground_color},
  423. {"normal-background-color",    0, 1, 0, 1, 1, 0, 0, NULL},
  424. {"reverse-foreground-color",   0, 1, 0, 1, 1, 0, 0, NULL},
  425. {"reverse-background-color",   0, 1, 0, 1, 1, 0, 0, NULL},
  426. #ifdef _WINDOWS
  427. {"font-name",                  0, 1, 0, 1, 1, 0, 0, "Name and size of font."},
  428. {"font-size",                  0, 1, 0, 1, 1, 0, 0, NULL},
  429. {"font-style",                 0, 1, 0, 1, 1, 0, 0, NULL},
  430. {"print-font-name",            0, 1, 0, 1, 1, 0, 0,
  431.                          "Name and size of printer font."},
  432. {"print-font-size",            0, 1, 0, 1, 1, 0, 0, NULL},
  433. {"print-font-style",           0, 1, 0, 1, 1, 0, 0, NULL},
  434. {"window-position",            0, 1, 0, 1, 1, 0, 0, cf_text_window_position},
  435. #endif    /* _WINDOWS */
  436. #endif    /* DOS */
  437. {NULL,                         0, 0, 0, 0, 0, 0, 0, NULL},
  438. };
  439.  
  440.  
  441. #if    defined(DOS) || defined(OS2)
  442. /*
  443.  * Table containing Code Page value to external charset value mappings
  444.  */
  445. unsigned char *xlate_to_codepage   = NULL;
  446. unsigned char *xlate_from_codepage = NULL;
  447. #endif
  448.  
  449.  
  450. static struct pinerc_line {
  451.   char *line;
  452.   struct variable *var;
  453.   unsigned int  is_var:1;
  454.   unsigned int  is_quoted:1;
  455.   unsigned int  obsolete_var:1;
  456. } *pinerc_lines = NULL;
  457.  
  458.  
  459. #ifdef    DEBUG
  460. /*
  461.  * Debug level and output file defined here, referenced globally.
  462.  * The debug file is opened and initialized below...
  463.  */
  464. int   debug    = DEFAULT_DEBUG;
  465. FILE *debugfile = NULL;
  466. #endif
  467.  
  468.  
  469. init_init_vars(ps)
  470.      struct pine *ps;
  471. {
  472.     ps->vars = variables;
  473. }
  474.  
  475.     
  476. /*----------------------------------------------------------------------
  477.      Initialize the variables
  478.  
  479.  Args:   ps   -- The usual pine structure
  480.  
  481.  Result: 
  482.  
  483.   This reads the system pine configuration file and the user's pine
  484. configuration file ".pinerc" and places the results in the variables 
  485. structure.  It sorts out what was read and sets a few other variables 
  486. based on the contents.
  487.   ----*/
  488. void 
  489. init_vars(ps)
  490.      struct pine *ps;
  491. {
  492.     char     buf[MAXPATH +1], *p, *q, **s;
  493.     register struct variable *vars = ps->vars;
  494.     int         obs_header_in_reply,     /* the obs_ variables are to       */
  495.          obs_old_style_reply,     /* support backwards compatibility */
  496.          obs_save_by_sender, i;
  497.     FeatureLevel obs_feature_level;
  498.  
  499.     /*--- The defaults here are defined in os-xxx.h so they can vary
  500.           per machine ---*/
  501.  
  502.     GLO_PRINTER            = cpystr(DF_DEFAULT_PRINTER);
  503.     GLO_ELM_STYLE_SAVE        = cpystr(DF_ELM_STYLE_SAVE);
  504.     GLO_SAVE_BY_SENDER        = cpystr(DF_SAVE_BY_SENDER);
  505.     GLO_HEADER_IN_REPLY        = cpystr(DF_HEADER_IN_REPLY);
  506.     GLO_INBOX_PATH        = cpystr("inbox");
  507.     GLO_DEFAULT_FCC        = cpystr(DF_DEFAULT_FCC);
  508.     GLO_DEFAULT_SAVE_FOLDER    = cpystr(DEFAULT_SAVE);
  509.     GLO_POSTPONED_FOLDER    = cpystr(POSTPONED_MSGS);
  510.     GLO_USE_ONLY_DOMAIN_NAME    = cpystr(DF_USE_ONLY_DOMAIN_NAME);
  511.     GLO_FEATURE_LEVEL        = cpystr(DF_FEATURE_LEVEL);
  512.     GLO_OLD_STYLE_REPLY        = cpystr(DF_OLD_STYLE_REPLY);
  513.     GLO_SORT_KEY        = cpystr(DF_SORT_KEY);
  514.     GLO_SAVED_MSG_NAME_RULE    = cpystr(DF_SAVED_MSG_NAME_RULE);
  515.     GLO_FCC_RULE        = cpystr(DF_FCC_RULE);
  516.     GLO_AB_SORT_RULE        = cpystr(DF_AB_SORT_RULE);
  517.     GLO_SIGNATURE_FILE        = cpystr(DF_SIGNATURE_FILE);
  518.     GLO_MAIL_DIRECTORY        = cpystr(DF_MAIL_DIRECTORY);
  519.     GLO_BUGS_FULLNAME        = cpystr(DF_BUGS_FULLNAME);
  520.     GLO_BUGS_ADDRESS        = cpystr(DF_BUGS_ADDRESS);
  521.     GLO_SUGGEST_FULLNAME    = cpystr(DF_SUGGEST_FULLNAME);
  522.     GLO_SUGGEST_ADDRESS        = cpystr(DF_SUGGEST_ADDRESS);
  523.     GLO_LOCAL_FULLNAME        = cpystr(DF_LOCAL_FULLNAME);
  524.     GLO_LOCAL_ADDRESS        = cpystr(DF_LOCAL_ADDRESS);
  525.     GLO_OVERLAP            = cpystr(DF_OVERLAP);
  526.     GLO_MARGIN            = cpystr(DF_MARGIN);
  527.     GLO_FILLCOL            = cpystr(DF_FILLCOL);
  528.     GLO_REPLY_STRING        = cpystr("> ");
  529.     GLO_EMPTY_HDR_MSG        = cpystr("Undisclosed recipients");
  530.     GLO_STATUS_MSG_DELAY    = cpystr("0");
  531.     GLO_MAILCHECK        = cpystr(DF_MAILCHECK);
  532.     GLO_KBLOCK_PASSWD_COUNT    = cpystr(DF_KBLOCK_PASSWD_COUNT);
  533. #ifdef    DF_FOLDER_EXTENSION
  534.     GLO_FOLDER_EXTENSION    = cpystr(DF_FOLDER_EXTENSION);
  535. #endif
  536. #ifdef    DF_SMTP_SERVER
  537.     GLO_SMTP_SERVER        = parse_list(DF_SMTP_SERVER, 1, NULL);
  538. #endif
  539.  
  540.     /*
  541.      * Default first value for addrbook list if none set.
  542.      * We also want to be sure to set global_val to the default
  543.      * if is_fixed, so that address-book= will cause the default to happen.
  544.      */
  545.     if(!GLO_ADDRESSBOOK && !FIX_ADDRESSBOOK)
  546.       GLO_ADDRESSBOOK = parse_list(DF_ADDRESSBOOK, 1, NULL);
  547.  
  548.     /*
  549.      * Default first value if none set.
  550.      */
  551.     if(!GLO_STANDARD_PRINTER && !FIX_STANDARD_PRINTER)
  552.       GLO_STANDARD_PRINTER = parse_list(DF_STANDARD_PRINTER, 1, NULL);
  553.  
  554. #if defined(DOS) || defined(OS2)
  555.     /*
  556.      * Rules for the config/support file locations under DOS are:
  557.      *
  558.      * 1) The location of the PINERC is searched for in the following
  559.      *    order of precedence:
  560.      *         - File pointed to by '-p' command line option
  561.      *       - File pointed to by PINERC environment variable
  562.      *       - $HOME\pine
  563.      *       - same dir as argv[0]
  564.      *
  565.      * 2) The HOME environment variable, if not set, defaults to 
  566.      *    root of the current working drive (see pine.c)
  567.      * 
  568.      * 3) The default for external files (PINE.SIG and ADDRBOOK) is the
  569.      *    same directory as the pinerc
  570.      *
  571.      * 4) The support files (PINE.HLP and PINE.NDX) are expected to be in
  572.      *    the same directory as PINE.EXE.
  573.      */
  574.     if(!ps_global->pinerc){
  575.     if(!(p = getenv("PINERC"))){
  576.         char buf2[MAXPATH];
  577.  
  578.         p = buf;        /* ultimately holds the answer */
  579.         build_path(buf2, ps_global->home_dir, DF_PINEDIR);
  580.         if(is_writable_dir(buf2) == 0){
  581.         /* $HOME/PINE/ exists!, see if $HOME/PINE/PINERC does too */
  582.         build_path(buf, buf2, SYSTEM_PINERC);
  583.         if(can_access(buf, ACCESS_EXISTS) != 0){
  584.             /*
  585.              * no $HOME/PINE/PINERC, make sure
  586.              * one isn't already in same dir as PINE.EXE
  587.              */
  588.             build_path(buf2, ps_global->pine_dir, SYSTEM_PINERC);
  589.             if(can_access(buf, ACCESS_EXISTS) == 0)
  590.               strcpy(buf, buf2);
  591.             /* else just create $HOME/PINEDIR/PINERC */
  592.         }
  593.         /* else just create $HOME/PINEDIR/PINERC */
  594.         }
  595.         else
  596.           /* no $HOME/pine dir, put PINERC next to PINE.EXE */
  597.           build_path(buf, ps_global->pine_dir, SYSTEM_PINERC);
  598.     }
  599.  
  600.     ps_global->pinerc = cpystr(p);
  601.     }
  602.  
  603.     /*
  604.      * Now that we know the default for the PINERC, build NEWSRC default.
  605.      * Backward compatibility makes this kind of funky.  If what the
  606.      * c-client thinks the NEWSRC should be exists *AND* it doesn't
  607.      * already exist in the PINERC's dir, use c-client's default, otherwise
  608.      * use the one next to the PINERC...
  609.      */
  610.     p = last_cmpnt(ps_global->pinerc);
  611.     buf[0] = '\0';
  612.     if(p != NULL){
  613.     strncpy(buf, ps_global->pinerc, p - ps_global->pinerc);
  614.     buf[p - ps_global->pinerc] = '\0';
  615.     }
  616.  
  617.     strcat(buf, "NEWSRC");
  618.  
  619.     if(!(p = (void *) mail_parameters(NULL, GET_NEWSRC, (void *)NULL))
  620.        || can_access(p, ACCESS_EXISTS) < 0
  621.        || can_access(buf, ACCESS_EXISTS) == 0){
  622.     mail_parameters(NULL, SET_NEWSRC, (void *)buf);
  623.     GLO_NEWSRC_PATH = cpystr(buf);
  624.     }
  625.     else
  626.       GLO_NEWSRC_PATH = cpystr(p);
  627.  
  628.     /*
  629.      * Now that we know where to look for the pinerc, see if a 
  630.      * pointer to the global pine.conf has been set.  If so,
  631.      * try loading it...
  632.      */
  633.     if(!ps_global->pine_conf && (p = getenv("PINECONF")))
  634.       ps_global->pine_conf = cpystr(p);
  635.  
  636.     if(ps_global->pine_conf)
  637.       read_pinerc(ps_global->pine_conf, vars, ParseGlobal);
  638.  
  639.     read_pinerc(ps_global->pinerc, vars, ParseLocal);
  640. #else
  641.     read_pinerc(ps_global->pine_conf ? ps_global->pine_conf
  642.                      : SYSTEM_PINERC,
  643.         vars,
  644.         ParseGlobal);
  645.  
  646.     if(!ps_global->pinerc){
  647.       build_path(buf, ps->home_dir, ".pinerc");
  648.       ps_global->pinerc = cpystr(buf);
  649.     }
  650.  
  651.     read_pinerc(ps_global->pinerc, vars, ParseLocal);
  652.     read_pinerc(SYSTEM_PINERC_FIXED, vars, ParseFixed);
  653. #endif
  654.  
  655.     set_current_val(&vars[V_INBOX_PATH], TRUE, TRUE);
  656.  
  657.     set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE);
  658.     if(VAR_USER_DOMAIN
  659.        && VAR_USER_DOMAIN[0]
  660.        && (p = strrindex(VAR_USER_DOMAIN, '@'))){
  661.     if(*(++p)){
  662.         char *q;
  663.  
  664.         sprintf(tmp_20k_buf,
  665.             "User-domain (%s) cannot contain \"@\", using \"%s\"",
  666.             VAR_USER_DOMAIN, p);
  667.         init_error(ps, tmp_20k_buf);
  668.         q = VAR_USER_DOMAIN;
  669.         while((*q++ = *p++) != '\0')
  670.           ;/* do nothing */
  671.     }
  672.     else{
  673.         sprintf(tmp_20k_buf,
  674.             "User-domain (%s) cannot contain \"@\", deleting",
  675.             VAR_USER_DOMAIN);
  676.         init_error(ps, tmp_20k_buf);
  677.         fs_give((void **)&USR_USER_DOMAIN);
  678.         set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE);
  679.     }
  680.     }
  681.  
  682.     set_current_val(&vars[V_USE_ONLY_DOMAIN_NAME], TRUE, TRUE);
  683.     set_current_val(&vars[V_REPLY_STRING], TRUE, TRUE);
  684.     set_current_val(&vars[V_EMPTY_HDR_MSG], TRUE, TRUE);
  685.  
  686.     /* obsolete, backwards compatibility */
  687.     set_current_val(&vars[V_HEADER_IN_REPLY], TRUE, TRUE);
  688.     obs_header_in_reply=!strucmp(VAR_HEADER_IN_REPLY, "yes");
  689.  
  690.     set_current_val(&vars[V_PRINTER], TRUE, TRUE);
  691.     set_current_val(&vars[V_PERSONAL_PRINT_COMMAND], TRUE, TRUE);
  692.     set_current_val(&vars[V_STANDARD_PRINTER], TRUE, TRUE);
  693.  
  694.     set_current_val(&vars[V_LAST_TIME_PRUNE_QUESTION], TRUE, TRUE);
  695.     if(VAR_LAST_TIME_PRUNE_QUESTION != NULL){
  696.         /* The month value in the file runs from 1-12, the variable here
  697.            runs from 0-11; the value in the file used to be 0-11, but we're 
  698.            fixing it in January */
  699.         ps->last_expire_year  = atoi(VAR_LAST_TIME_PRUNE_QUESTION);
  700.         ps->last_expire_month =
  701.             atoi(strindex(VAR_LAST_TIME_PRUNE_QUESTION, '.') + 1);
  702.         if(ps->last_expire_month == 0){
  703.             /* Fix for 0 because of old bug */
  704.             char buf[10];
  705.             sprintf(buf, "%d.%d", ps_global->last_expire_year,
  706.               ps_global->last_expire_month + 1);
  707.             set_variable(V_LAST_TIME_PRUNE_QUESTION, buf, 1);
  708.         }else{
  709.             ps->last_expire_month--; 
  710.         } 
  711.     }else{
  712.         ps->last_expire_year  = -1;
  713.         ps->last_expire_month = -1;
  714.     }
  715.  
  716.     set_current_val(&vars[V_BUGS_FULLNAME], TRUE, TRUE);
  717.     set_current_val(&vars[V_BUGS_ADDRESS], TRUE, TRUE);
  718.     set_current_val(&vars[V_SUGGEST_FULLNAME], TRUE, TRUE);
  719.     set_current_val(&vars[V_SUGGEST_ADDRESS], TRUE, TRUE);
  720.     set_current_val(&vars[V_LOCAL_FULLNAME], TRUE, TRUE);
  721.     set_current_val(&vars[V_LOCAL_ADDRESS], TRUE, TRUE);
  722.     set_current_val(&vars[V_BUGS_EXTRAS], TRUE, TRUE);
  723.     set_current_val(&vars[V_KBLOCK_PASSWD_COUNT], TRUE, TRUE);
  724.     set_current_val(&vars[V_DEFAULT_FCC], TRUE, TRUE);
  725.     set_current_val(&vars[V_POSTPONED_FOLDER], TRUE, TRUE);
  726.     set_current_val(&vars[V_READ_MESSAGE_FOLDER], TRUE, TRUE);
  727.     set_current_val(&vars[V_EDITOR], TRUE, TRUE);
  728.     set_current_val(&vars[V_SPELLER], TRUE, TRUE);
  729.     set_current_val(&vars[V_IMAGE_VIEWER], TRUE, TRUE);
  730.     set_current_val(&vars[V_SMTP_SERVER], TRUE, TRUE);
  731.     set_current_val(&vars[V_COMP_HDRS], TRUE, TRUE);
  732.     set_current_val(&vars[V_CUSTOM_HDRS], TRUE, TRUE);
  733.     set_current_val(&vars[V_SENDMAIL_PATH], TRUE, TRUE);
  734.     set_current_val(&vars[V_DISPLAY_FILTERS], TRUE, TRUE);
  735.     set_current_val(&vars[V_SEND_FILTER], TRUE, TRUE);
  736.     set_current_val(&vars[V_ALT_ADDRS], TRUE, TRUE);
  737.     set_current_val(&vars[V_ABOOK_FORMATS], TRUE, TRUE);
  738.  
  739.     set_current_val(&vars[V_OPER_DIR], TRUE, TRUE);
  740.     if(VAR_OPER_DIR && !VAR_OPER_DIR[0]){
  741.     init_error(ps,
  742.  "Setting operating-dir to the empty string is not allowed.  Will be ignored!");
  743.     fs_give((void **)&VAR_OPER_DIR);
  744.     if(FIX_OPER_DIR)
  745.       fs_give((void **)&FIX_OPER_DIR);
  746.     if(GLO_OPER_DIR)
  747.       fs_give((void **)&GLO_OPER_DIR);
  748.     if(COM_OPER_DIR)
  749.       fs_give((void **)&COM_OPER_DIR);
  750.     if(USR_OPER_DIR)
  751.       fs_give((void **)&USR_OPER_DIR);
  752.     }
  753.  
  754.     set_current_val(&vars[V_INDEX_FORMAT], TRUE, TRUE);
  755.     init_index_format(VAR_INDEX_FORMAT, &ps->index_disp_format);
  756.  
  757.     set_current_val(&vars[V_PERSONAL_PRINT_CATEGORY], TRUE, TRUE);
  758.     ps->printer_category = -1;
  759.     if(VAR_PERSONAL_PRINT_CATEGORY != NULL){
  760.     ps->printer_category = atoi(VAR_PERSONAL_PRINT_CATEGORY);
  761.     if(ps->printer_category < 1 || ps->printer_category > 3){
  762.         char **tt;
  763.         char aname[100];
  764.  
  765.         strcat(strcpy(aname, ANSI_PRINTER), "-no-formfeed");
  766.         if(strucmp(VAR_PRINTER, ANSI_PRINTER) == 0
  767.           || strucmp(VAR_PRINTER, aname) == 0)
  768.           ps->printer_category = 1;
  769.         else if(VAR_STANDARD_PRINTER && VAR_STANDARD_PRINTER[0]){
  770.         for(tt = VAR_STANDARD_PRINTER; *tt; tt++)
  771.           if(strucmp(VAR_PRINTER, *tt) == 0)
  772.             break;
  773.         
  774.         if(*tt)
  775.           ps->printer_category = 2;
  776.         }
  777.  
  778.         /* didn't find it yet */
  779.         if(ps->printer_category < 1 || ps->printer_category > 3){
  780.         if(VAR_PERSONAL_PRINT_COMMAND && VAR_PERSONAL_PRINT_COMMAND[0]){
  781.             for(tt = VAR_PERSONAL_PRINT_COMMAND; *tt; tt++)
  782.               if(strucmp(VAR_PRINTER, *tt) == 0)
  783.             break;
  784.             
  785.             if(*tt)
  786.               ps->printer_category = 3;
  787.         }
  788.         }
  789.     }
  790.     }
  791.  
  792.     set_current_val(&vars[V_OVERLAP], TRUE, TRUE);
  793.     ps->viewer_overlap = i = atoi(DF_OVERLAP);
  794.     if(SVAR_OVERLAP(ps, i, tmp_20k_buf))
  795.       init_error(ps, tmp_20k_buf);
  796.     else
  797.       ps->viewer_overlap = i;
  798.  
  799.     set_current_val(&vars[V_MARGIN], TRUE, TRUE);
  800.     ps->scroll_margin = i = atoi(DF_MARGIN);
  801.     if(SVAR_MARGIN(ps, i, tmp_20k_buf))
  802.       init_error(ps, tmp_20k_buf);
  803.     else
  804.       ps->scroll_margin = i;
  805.  
  806.     set_current_val(&vars[V_FILLCOL], TRUE, TRUE);
  807.     ps->composer_fillcol = i = atoi(DF_FILLCOL);
  808.     if(SVAR_FILLCOL(ps, i, tmp_20k_buf))
  809.       init_error(ps, tmp_20k_buf);
  810.     else
  811.       ps->composer_fillcol = i;
  812.     
  813.     set_current_val(&vars[V_STATUS_MSG_DELAY], TRUE, TRUE);
  814.     ps->status_msg_delay = i = 0;
  815.     if(SVAR_MSGDLAY(ps, i, tmp_20k_buf))
  816.       init_error(ps, tmp_20k_buf);
  817.     else
  818.       ps->status_msg_delay = i;
  819.  
  820.     /* timeout is a regular extern int because it is referenced in pico */
  821.     set_current_val(&vars[V_MAILCHECK], TRUE, TRUE);
  822.     timeout = i = 15;
  823.     if(SVAR_MAILCHK(ps, i, tmp_20k_buf))
  824.       init_error(ps, tmp_20k_buf);
  825.     else
  826.       timeout = i;
  827.  
  828.     set_current_val(&vars[V_NEWSRC_PATH], TRUE, TRUE);
  829.     if(ps_global->VAR_NEWSRC_PATH && ps_global->VAR_NEWSRC_PATH[0])
  830.       mail_parameters(NULL, SET_NEWSRC, (void *)ps_global->VAR_NEWSRC_PATH);
  831.  
  832.     set_current_val(&vars[V_NEWS_ACTIVE_PATH], TRUE, TRUE);
  833.     if(ps_global->VAR_NEWS_ACTIVE_PATH)
  834.       mail_parameters(NULL, SET_NEWSACTIVE,
  835.               (void *)ps_global->VAR_NEWS_ACTIVE_PATH);
  836.  
  837.     set_current_val(&vars[V_NEWS_SPOOL_DIR], TRUE, TRUE);
  838.     if(ps_global->VAR_NEWS_SPOOL_DIR)
  839.       mail_parameters(NULL, SET_NEWSSPOOL,
  840.               (void *)ps_global->VAR_NEWS_SPOOL_DIR);
  841.  
  842.     /* guarantee a save default */
  843.     set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE);
  844.     if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0])
  845.       set_variable(V_DEFAULT_SAVE_FOLDER,
  846.            (GLO_DEFAULT_SAVE_FOLDER && GLO_DEFAULT_SAVE_FOLDER[0])
  847.              ? GLO_DEFAULT_SAVE_FOLDER
  848.              : DEFAULT_SAVE, 0);
  849.  
  850.     /* obsolete, backwards compatibility */
  851.     set_current_val(&vars[V_FEATURE_LEVEL], TRUE, TRUE);
  852.     if(strucmp(VAR_FEATURE_LEVEL, "seedling") == 0)
  853.       obs_feature_level = Seedling;
  854.     else if(strucmp(VAR_FEATURE_LEVEL, "old-growth") == 0)
  855.       obs_feature_level = Seasoned;
  856.     else
  857.       obs_feature_level = Sapling;
  858.  
  859.     /* obsolete, backwards compatibility */
  860.     set_current_val(&vars[V_OLD_STYLE_REPLY], TRUE, TRUE);
  861.     obs_old_style_reply = !strucmp(VAR_OLD_STYLE_REPLY, "yes");
  862.  
  863.     set_current_val(&vars[V_SIGNATURE_FILE], TRUE, TRUE);
  864.     set_current_val(&vars[V_CHAR_SET], TRUE, TRUE);
  865.     set_current_val(&vars[V_GLOB_ADDRBOOK], TRUE, TRUE);
  866.     set_current_val(&vars[V_ADDRESSBOOK], TRUE, TRUE);
  867.     set_current_val(&vars[V_FORCED_ABOOK_ENTRY], TRUE, TRUE);
  868.     set_current_val(&vars[V_NNTP_SERVER], TRUE, TRUE);
  869.     for(i = 0; VAR_NNTP_SERVER && VAR_NNTP_SERVER[i]; i++)
  870.       removing_quotes(VAR_NNTP_SERVER[i]);
  871.  
  872.  
  873.     set_current_val(&vars[V_VIEW_HEADERS], TRUE, TRUE);
  874.     /* strip spaces and colons */
  875.     if(ps->VAR_VIEW_HEADERS){
  876.     for(s = ps->VAR_VIEW_HEADERS; (q = *s) != NULL; s++){
  877.         if(q[0]){
  878.         removing_leading_white_space(q);
  879.         /* look for colon or space or end */
  880.         for(p = q; *p && !isspace((unsigned char)*p) && *p != ':'; p++)
  881.           ;/* do nothing */
  882.         
  883.         *p = '\0';
  884.         if(strucmp(q, ALL_EXCEPT) == 0)
  885.           ps->view_all_except = 1;
  886.         }
  887.     }
  888.     }
  889.  
  890.     set_current_val(&vars[V_UPLOAD_CMD], TRUE, TRUE);
  891.     set_current_val(&vars[V_UPLOAD_CMD_PREFIX], TRUE, TRUE);
  892.     set_current_val(&vars[V_DOWNLOAD_CMD], TRUE, TRUE);
  893.     set_current_val(&vars[V_DOWNLOAD_CMD_PREFIX], TRUE, TRUE);
  894.     set_current_val(&vars[V_MAILCAP_PATH], TRUE, TRUE);
  895.     set_current_val(&vars[V_MIMETYPE_PATH], TRUE, TRUE);
  896.     set_current_val(&vars[V_TCPOPENTIMEO], TRUE, TRUE);
  897.     set_current_val(&vars[V_RSHOPENTIMEO], TRUE, TRUE);
  898.  
  899. #ifdef NEWBB
  900.     set_current_val(&vars[V_NNTP_NEW_GROUP_TIME], TRUE, TRUE);
  901.     if(ps_global->VAR_NNTP_NEW_GROUP_TIME == NULL ||
  902.        strlen(ps_global->VAR_NNTP_NEW_GROUP_TIME) <= 0){
  903.         /* If not set, set to two weeks ago */
  904.         long now  = time(0) - 24 * 3600 * 14; /* Two weeks ago */
  905.         set_variable(V_NNTP_NEW_GROUP_TIME, ctime(&now), 0);
  906.     }
  907. #endif
  908.  
  909. #if    defined(DOS) || defined(OS2)
  910.     /*
  911.      * Handle setting up page table IF running DOS 3.30 or greater
  912.      */
  913.     if(strucmp(ps_global->VAR_CHAR_SET, "us-ascii") != 0){
  914. #ifdef    DOS
  915.     unsigned long ver;
  916.         extern unsigned int dos_version();
  917.         extern          int dos_codepage();
  918. #endif
  919.     extern unsigned char *read_xtable();
  920. #ifndef    _WINDOWS
  921.     extern unsigned char  cp437L1[], cp850L1[], cp860L1[], cp863L1[],
  922.                   cp865L1[], cp866L5[], cp852L2[], cp895L2[];
  923.     extern unsigned char  L1cp437[], L1cp850[], L1cp860[], L1cp863[],
  924.                   L1cp865[], L5cp866[], L2cp852[], L2cp895[];
  925. #endif
  926.  
  927.     /* suck in the translation table */
  928. #if    defined(DOS) && !defined(_WINDOWS)
  929.     if(((ver = dos_version()) & 0x00ff) > 3 
  930.        || ((ver & 0x00ff) == 3 && (ver >> 8) >= 30))
  931. #endif
  932.     {
  933.         char *in_table  = getenv("ISO_TO_CP"),
  934.              *out_table = getenv("CP_TO_ISO");
  935.  
  936.         if(out_table)
  937.           xlate_from_codepage = read_xtable(out_table);
  938.  
  939.         if(in_table)
  940.           xlate_to_codepage = read_xtable(in_table);
  941.  
  942. #ifndef    _WINDOWS
  943.         /*
  944.          * if tables not already set, do the best we can...
  945.          */
  946.         switch(dos_codepage()){
  947.           case 437: /* latin-1 */
  948.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-1") == 0){
  949.             if(!xlate_from_codepage)
  950.               xlate_from_codepage = cp437L1;
  951.  
  952.             if(!xlate_to_codepage)
  953.               xlate_to_codepage   = L1cp437;
  954.         }
  955.  
  956.         break;
  957.           case 850: /* latin-1 */
  958.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-1") == 0){
  959.             if(!xlate_from_codepage)
  960.               xlate_from_codepage = cp850L1;
  961.  
  962.             if(!xlate_to_codepage)
  963.               xlate_to_codepage = L1cp850;
  964.         }
  965.  
  966.         break;
  967.           case 860: /* latin-1 */
  968.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-1") == 0){
  969.             if(!xlate_from_codepage)
  970.               xlate_from_codepage = cp860L1;
  971.  
  972.             if(!xlate_to_codepage)
  973.               xlate_to_codepage   = L1cp860;
  974.         }
  975.  
  976.         break;
  977.           case 863: /* latin-1 */
  978.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-1") == 0){
  979.             if(!xlate_from_codepage)
  980.               xlate_from_codepage = cp863L1;
  981.  
  982.             if(!xlate_to_codepage)
  983.               xlate_to_codepage   = L1cp863;
  984.         }
  985.  
  986.         break;
  987.           case 865: /* latin-1 */
  988.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-1") == 0){
  989.             if(!xlate_from_codepage)
  990.               xlate_from_codepage = cp865L1;
  991.  
  992.             if(!xlate_to_codepage)
  993.               xlate_to_codepage   = L1cp865;
  994.         }
  995.  
  996.         break;
  997.           case 866: /* latin-5 */
  998.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-5") == 0){
  999.             if(!xlate_from_codepage)
  1000.               xlate_from_codepage = cp866L5;
  1001.  
  1002.             if(!xlate_to_codepage)
  1003.               xlate_to_codepage   = L5cp866;
  1004.         }
  1005.  
  1006.         break;
  1007.           case 852: /* latin-2 */
  1008.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-2") == 0){
  1009.             if(!xlate_from_codepage)
  1010.               xlate_from_codepage = cp852L2;
  1011.  
  1012.             if(!xlate_to_codepage)
  1013.               xlate_to_codepage = L2cp852;
  1014.         }
  1015.  
  1016.         break;
  1017.           case 895: /* latin-2 */
  1018.         if(strucmp(ps_global->VAR_CHAR_SET, "iso-8859-2") == 0){
  1019.             if(!xlate_from_codepage)
  1020.               xlate_from_codepage = cp895L2;
  1021.  
  1022.             if(!xlate_to_codepage)
  1023.               xlate_to_codepage = L2cp895;
  1024.         }
  1025.  
  1026.         break;
  1027.           default:
  1028.         break;
  1029.         }
  1030. #endif    /* _WINDOWS */
  1031.     }
  1032.     }
  1033.  
  1034.     set_current_val(&vars[V_FOLDER_EXTENSION], TRUE, TRUE);
  1035.     if(ps_global->VAR_FOLDER_EXTENSION)
  1036.       mail_parameters(NULL, SET_EXTENSION, 
  1037.               (void *)ps_global->VAR_FOLDER_EXTENSION);
  1038.  
  1039.     set_current_val(&vars[V_NORM_FORE_COLOR], TRUE, TRUE);
  1040.     if(ps_global->VAR_NORM_FORE_COLOR)
  1041.       pico_nfcolor(ps_global->VAR_NORM_FORE_COLOR);
  1042.  
  1043.     set_current_val(&vars[V_NORM_BACK_COLOR], TRUE, TRUE);
  1044.     if(ps_global->VAR_NORM_BACK_COLOR)
  1045.       pico_nbcolor(ps_global->VAR_NORM_BACK_COLOR);
  1046.  
  1047.     set_current_val(&vars[V_REV_FORE_COLOR], TRUE, TRUE);
  1048.     if(ps_global->VAR_REV_FORE_COLOR)
  1049.       pico_rfcolor(ps_global->VAR_REV_FORE_COLOR);
  1050.  
  1051.     set_current_val(&vars[V_REV_BACK_COLOR], TRUE, TRUE);
  1052.     if(ps_global->VAR_REV_BACK_COLOR)
  1053.       pico_rbcolor(ps_global->VAR_REV_BACK_COLOR);
  1054.  
  1055. #ifdef    _WINDOWS
  1056.     set_current_val(&vars[V_FONT_NAME], TRUE, TRUE);
  1057.     set_current_val(&vars[V_FONT_SIZE], TRUE, TRUE);
  1058.     set_current_val(&vars[V_FONT_STYLE], TRUE, TRUE);
  1059.     set_current_val(&vars[V_WINDOW_POSITION], TRUE, TRUE);
  1060.  
  1061.     mswin_setwindow (ps_global->VAR_FONT_NAME, ps_global->VAR_FONT_SIZE, 
  1062.             ps_global->VAR_FONT_STYLE, ps_global->VAR_WINDOW_POSITION);
  1063.     set_current_val(&vars[V_PRINT_FONT_NAME], TRUE, TRUE);
  1064.     set_current_val(&vars[V_PRINT_FONT_SIZE], TRUE, TRUE);
  1065.     set_current_val(&vars[V_PRINT_FONT_STYLE], TRUE, TRUE);
  1066.     mswin_setprintfont (ps_global->VAR_PRINT_FONT_NAME,
  1067.             ps_global->VAR_PRINT_FONT_SIZE,
  1068.             ps_global->VAR_PRINT_FONT_STYLE);
  1069.  
  1070.     {
  1071.     char **help_text = get_help_text (h_pine_for_windows, NULL);
  1072.  
  1073.     if (help_text != NULL)
  1074.       mswin_sethelptext ("PC-Pine For Windows", NULL, 0, help_text);
  1075.     }
  1076.  
  1077.     mswin_setclosetext ("Use the \"Q\" command to exit Pine.");
  1078. #endif    /* _WINDOWS */
  1079. #endif    /* DOS */
  1080.  
  1081.     set_current_val(&vars[V_LAST_VERS_USED], TRUE, TRUE);
  1082.     /* Check for special cases first */
  1083.     if(VAR_LAST_VERS_USED
  1084.           /* Special Case #1: 3.92 use is effectively the same as 3.92 */
  1085.        && (strcmp(VAR_LAST_VERS_USED, "3.92") == 0
  1086.        /*
  1087.         * Special Case #2:  We're not really a new version if our
  1088.         * version number looks like: <number><dot><number><number><alpha>
  1089.         * The <alpha> on the end is key meaning its just a bug-fix patch.
  1090.         */
  1091.        || (isdigit((unsigned char)PINE_VERSION[0])
  1092.            && PINE_VERSION[1] == '.'
  1093.            && isdigit((unsigned char)PINE_VERSION[2])
  1094.            && isdigit((unsigned char)PINE_VERSION[3])
  1095.            && isalpha((unsigned char)PINE_VERSION[4])
  1096.            && strncmp(VAR_LAST_VERS_USED, PINE_VERSION, 4) >= 0))){
  1097.     ps->show_new_version = 0;
  1098.     set_variable(V_LAST_VERS_USED, pine_version, 1);
  1099.     }
  1100.     /* Otherwise just do lexicographic comparision... */
  1101.     else if(VAR_LAST_VERS_USED
  1102.         && strcmp(VAR_LAST_VERS_USED, PINE_VERSION) >= 0){
  1103.     ps->show_new_version = 0;
  1104.     }
  1105.     else{
  1106.         ps->pre390 = !(VAR_LAST_VERS_USED
  1107.                && strcmp(VAR_LAST_VERS_USED, "3.90") >= 0);
  1108.  
  1109.     /*
  1110.      * Don't offer the new version message if we're told not to.
  1111.      */
  1112.     set_current_val(&vars[V_NEW_VER_QUELL], TRUE, TRUE);
  1113.     ps->show_new_version = !(VAR_NEW_VER_QUELL
  1114.                      && strcmp(pine_version,
  1115.                        VAR_NEW_VER_QUELL) < 0);
  1116.  
  1117.     set_variable(V_LAST_VERS_USED, pine_version, 1);
  1118.     }
  1119.  
  1120.     /* Obsolete, backwards compatibility */
  1121.     set_current_val(&vars[V_ELM_STYLE_SAVE], TRUE, TRUE);
  1122.     /* Also obsolete */
  1123.     set_current_val(&vars[V_SAVE_BY_SENDER], TRUE, TRUE);
  1124.     if(!strucmp(VAR_ELM_STYLE_SAVE, "yes"))
  1125.       set_variable(V_SAVE_BY_SENDER, "yes", 1);
  1126.     obs_save_by_sender = !strucmp(VAR_SAVE_BY_SENDER, "yes");
  1127.  
  1128.     /*
  1129.      * mail-directory variable is obsolete, put its value in
  1130.      * folder-collection list if that list is blank...
  1131.      */
  1132.     set_current_val(&vars[V_MAIL_DIRECTORY], TRUE, TRUE);
  1133.     set_current_val(&vars[V_PRUNED_FOLDERS], TRUE, TRUE);
  1134.     set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE);
  1135.     set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE);
  1136.     set_current_val(&vars[V_NEWS_SPEC], TRUE, TRUE);
  1137.     set_current_val(&vars[V_FOLDER_SPEC], TRUE, TRUE);
  1138.     /*
  1139.      * If there's no spec'd folder collection somewhere, set the current
  1140.      * value to the default, built from the mail directory...
  1141.      */
  1142.     if(!VAR_FOLDER_SPEC){
  1143.     build_path(tmp_20k_buf, VAR_MAIL_DIRECTORY, "[]");
  1144.     VAR_FOLDER_SPEC = parse_list(tmp_20k_buf, 1, NULL);
  1145.     }
  1146.  
  1147.     set_current_val(&vars[V_SORT_KEY], TRUE, TRUE);
  1148.     if(decode_sort(ps, VAR_SORT_KEY) == -1){
  1149.         if(!struncmp(VAR_SORT_KEY, "to", 2) ||
  1150.         !struncmp(VAR_SORT_KEY, "cc", 2)){
  1151.        sprintf(tmp_20k_buf, "Sort type \"%s\" is not implemented yet\n",
  1152.            VAR_SORT_KEY);
  1153.        init_error(ps, tmp_20k_buf);
  1154.     }else{
  1155.        fprintf(stderr, "Sort type \"%s\" is invalid\n", VAR_SORT_KEY);
  1156.        exit(-1);
  1157.     }
  1158.     }
  1159.  
  1160.     set_current_val(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE);
  1161.     {int        i;
  1162.      NAMEVAL_S *v;
  1163.      for(i = 0; v = save_msg_rules(i); i++)
  1164.        if(!strucmp(VAR_SAVED_MSG_NAME_RULE, v->name)){
  1165.        if((ps_global->save_msg_rule = v->value) == MSG_RULE_DEFLT){
  1166.            if(!strucmp(USR_SAVED_MSG_NAME_RULE,
  1167.                v->name))
  1168.          obs_save_by_sender = 0;  /* don't overwrite */
  1169.        }
  1170.  
  1171.        break;
  1172.        }
  1173.     }
  1174.  
  1175.     set_current_val(&vars[V_FCC_RULE], TRUE, TRUE);
  1176.     {int        i;
  1177.      NAMEVAL_S *v;
  1178.      for(i = 0; v = fcc_rules(i); i++)
  1179.        if(!strucmp(VAR_FCC_RULE, v->name)){
  1180.        ps_global->fcc_rule = v->value;
  1181.        break;
  1182.        }
  1183.     }
  1184.  
  1185.     set_current_val(&vars[V_AB_SORT_RULE], TRUE, TRUE);
  1186.     {int        i;
  1187.      NAMEVAL_S *v;
  1188.      for(i = 0; v = ab_sort_rules(i); i++)
  1189.        if(!strucmp(VAR_AB_SORT_RULE, v->name)){
  1190.        ps_global->ab_sort_rule = v->value;
  1191.        break;
  1192.        }
  1193.     }
  1194.  
  1195.     set_current_val(&vars[V_GOTO_DEFAULT_RULE], TRUE, TRUE);
  1196.     {int        i;
  1197.      NAMEVAL_S *v;
  1198.      for(i = 0; v = goto_rules(i); i++)
  1199.        if(!strucmp(VAR_GOTO_DEFAULT_RULE, v->name)){
  1200.        ps_global->goto_default_rule = v->value;
  1201.        break;
  1202.        }
  1203.     }
  1204.  
  1205.     /* backwards compatibility */
  1206.     if(obs_save_by_sender){
  1207.         ps->save_msg_rule = MSG_RULE_FROM;
  1208.     set_variable(V_SAVED_MSG_NAME_RULE, "by-from", 1);
  1209.     }
  1210.  
  1211.     set_feature_list_current_val(&vars[V_FEATURE_LIST]);
  1212.     process_feature_list(ps, VAR_FEATURE_LIST,
  1213.            (obs_feature_level == Seasoned) ? 1 : 0,
  1214.        obs_header_in_reply, obs_old_style_reply);
  1215.  
  1216.     /* this should come after process_feature_list because of use_fkeys */
  1217.     if(!ps->start_in_index)
  1218.         set_current_val(&vars[V_INIT_CMD_LIST], FALSE, TRUE);
  1219.     if(VAR_INIT_CMD_LIST)
  1220.         process_init_cmds(ps, VAR_INIT_CMD_LIST);
  1221.  
  1222. #ifdef    _WINDOWS
  1223.     mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
  1224. #endif    /* _WINDOWS */
  1225.  
  1226. #ifdef DEBUG
  1227.     if(debugfile){
  1228.     gf_io_t pc;
  1229.  
  1230.     gf_set_writec(&pc, debugfile, 0L, FileStar);
  1231.     if(do_debug(debugfile))
  1232.       dump_config(ps, pc);
  1233.     }
  1234. #endif /* DEBUG */
  1235. }
  1236.  
  1237.  
  1238. /*
  1239.  * free_vars -- give back resources acquired when we defined the
  1240.  *        variables list
  1241.  */
  1242. void
  1243. free_vars(ps)
  1244.     struct pine *ps;
  1245. {
  1246.     register int i;
  1247.  
  1248.     for(i = 0; ps && i <= V_LAST_VAR; i++)
  1249.       if(ps->vars[i].is_list){
  1250.       char **l;
  1251.  
  1252.       if(l = ps->vars[i].current_val.l){
  1253.           for( ; *l; l++)
  1254.         fs_give((void **)l);
  1255.  
  1256.           fs_give((void **)&ps->vars[i].current_val.l);
  1257.       }
  1258.  
  1259.       if(l = ps->vars[i].user_val.l){
  1260.           for( ; *l; l++)
  1261.         fs_give((void **)l);
  1262.  
  1263.           fs_give((void **)&ps->vars[i].user_val.l);
  1264.       }
  1265.  
  1266.       if(l = ps->vars[i].global_val.l){
  1267.           for( ; *l; l++)
  1268.         fs_give((void **)l);
  1269.  
  1270.           fs_give((void **)&ps->vars[i].global_val.l);
  1271.       }
  1272.  
  1273.       if(l = ps->vars[i].fixed_val.l){
  1274.           for( ; *l; l++)
  1275.         fs_give((void **)l);
  1276.  
  1277.           fs_give((void **)&ps->vars[i].fixed_val.l);
  1278.       }
  1279.  
  1280.       if(l = ps->vars[i].cmdline_val.l){
  1281.           for( ; *l; l++)
  1282.         fs_give((void **)l);
  1283.  
  1284.           fs_give((void **)&ps->vars[i].cmdline_val.l);
  1285.       }
  1286.       }
  1287.       else{
  1288.       if(ps->vars[i].current_val.p)
  1289.         fs_give((void **)&ps->vars[i].current_val.p);
  1290.  
  1291.       if(ps->vars[i].user_val.p)
  1292.         fs_give((void **)&ps->vars[i].user_val.p);
  1293.  
  1294.       if(ps->vars[i].global_val.p)
  1295.         fs_give((void **)&ps->vars[i].global_val.p);
  1296.  
  1297.       if(ps->vars[i].fixed_val.p)
  1298.         fs_give((void **)&ps->vars[i].fixed_val.p);
  1299.  
  1300.       if(ps->vars[i].cmdline_val.p)
  1301.         fs_give((void **)&ps->vars[i].cmdline_val.p);
  1302.       }
  1303. }
  1304.  
  1305.  
  1306. /*
  1307.  * Standard way to get at feature list members...
  1308.  */
  1309. NAMEVAL_S *
  1310. feature_list(index)
  1311.     int index;
  1312. {
  1313.     /*
  1314.      * This list is alphabatized by feature string, but the 
  1315.      * macro values need not be ordered.
  1316.      */
  1317.     static NAMEVAL_S feat_list[] = {
  1318.     {"old-growth",                F_OLD_GROWTH},
  1319. #if !defined(DOS) && !defined(OS2)
  1320.     {"allow-talk",                F_ALLOW_TALK},
  1321. #endif
  1322.     {"assume-slow-link",            F_FORCE_LOW_SPEED},
  1323.     {"auto-move-read-msgs",            F_AUTO_READ_MSGS},
  1324.     {"auto-open-next-unread",        F_AUTO_OPEN_NEXT_UNREAD},
  1325.     {"auto-zoom-after-select",        F_AUTO_ZOOM},
  1326.     {"auto-unzoom-after-apply",        F_AUTO_UNZOOM},
  1327.     {"compose-cut-from-cursor",        F_DEL_FROM_DOT},
  1328.     {"compose-maps-delete-key-to-ctrl-d",    F_COMPOSE_MAPS_DEL},
  1329.     {"compose-rejects-unqualified-addrs",    F_COMPOSE_REJECTS_UNQUAL},
  1330.     {"compose-send-offers-first-filter",    F_FIRST_SEND_FILTER_DFLT},
  1331.     {"compose-sets-newsgroup-without-confirm", F_COMPOSE_TO_NEWSGRP},
  1332.     {"delete-skips-deleted",        F_DEL_SKIPS_DEL},
  1333.     {"disable-config-cmd",            F_DISABLE_CONFIG_SCREEN},
  1334.     {"disable-default-in-bug-report",    F_DISABLE_DFLT_IN_BUG_RPT},
  1335.     {"disable-busy-alarm",            F_DISABLE_ALARM},
  1336.     {"disable-keyboard-lock-cmd",        F_DISABLE_KBLOCK_CMD},
  1337.     {"disable-keymenu",            F_BLANK_KEYMENU},
  1338.     {"disable-password-cmd",        F_DISABLE_PASSWORD_CMD},
  1339.     {"disable-update-cmd",            F_DISABLE_UPDATE_CMD},
  1340.     {"disable-signature-edit-cmd",        F_DISABLE_SIGEDIT_CMD},
  1341.     {"enable-8bit-esmtp-negotiation",    F_ENABLE_8BIT},
  1342.     {"enable-8bit-nntp-posting",        F_ENABLE_8BIT_NNTP},
  1343.     {"enable-aggregate-command-set",    F_ENABLE_AGG_OPS},
  1344.     {"enable-alternate-editor-cmd",        F_ENABLE_ALT_ED},
  1345.     {"enable-alternate-editor-implicitly",    F_ALT_ED_NOW},
  1346. #ifdef    BACKGROUND_POST
  1347.     {"enable-background-sending",        F_BACKGROUND_POST},
  1348. #endif
  1349.     {"enable-bounce-cmd",            F_ENABLE_BOUNCE},
  1350.     {"enable-cruise-mode",            F_ENABLE_SPACE_AS_TAB},
  1351.     {"enable-cruise-mode-delete",        F_ENABLE_TAB_DELETES},
  1352.     {"enable-dot-files",            F_ENABLE_DOT_FILES},
  1353.     {"enable-dot-folders",            F_ENABLE_DOT_FOLDERS},
  1354.     {"enable-flag-cmd",            F_ENABLE_FLAG},
  1355.     {"enable-flag-screen-implicitly",    F_FLAG_SCREEN_DFLT},
  1356.     {"enable-full-header-cmd",        F_ENABLE_FULL_HDR},
  1357.     {"enable-goto-in-file-browser",        F_ALLOW_GOTO},
  1358.     {"enable-incoming-folders",        F_ENABLE_INCOMING},
  1359.     {"enable-jump-shortcut",        F_ENABLE_JUMP},
  1360.     {"enable-mail-check-cue",        F_SHOW_DELAY_CUE},
  1361.     {"enable-mouse-in-xterm",        F_ENABLE_MOUSE},
  1362.     {"enable-newmail-in-xterm-icon",    F_ENABLE_XTERM_NEWMAIL},
  1363.     {"enable-suspend",            F_CAN_SUSPEND},
  1364.     {"enable-tab-completion",        F_ENABLE_TAB_COMPLETE},
  1365.     {"enable-unix-pipe-cmd",        F_ENABLE_PIPE},
  1366.     {"enable-verbose-smtp-posting",        F_VERBOSE_POST},
  1367.     {"expanded-view-of-addressbooks",    F_EXPANDED_ADDRBOOKS},
  1368.     {"expanded-view-of-distribution-lists",    F_EXPANDED_DISTLISTS},
  1369.     {"expanded-view-of-folders",        F_EXPANDED_FOLDERS},
  1370.     {"expunge-without-confirm",        F_AUTO_EXPUNGE},
  1371.     {"fcc-on-bounce",            F_FCC_ON_BOUNCE},
  1372.     {"include-attachments-in-reply",    F_ATTACHMENTS_IN_REPLY},
  1373.     {"include-header-in-reply",        F_INCLUDE_HEADER},
  1374.     {"include-text-in-reply",        F_AUTO_INCLUDE_IN_REPLY},
  1375.     {"news-approximates-new-status",    F_FAKE_NEW_IN_NEWS},
  1376.     {"news-post-without-validation",    F_NO_NEWS_VALIDATION},
  1377.     {"news-read-in-newsrc-order",        F_READ_IN_NEWSRC_ORDER},
  1378.     {"pass-control-characters-as-is",    F_PASS_CONTROL_CHARS},
  1379.     {"preserve-start-stop-characters",    F_PRESERVE_START_STOP},
  1380.     {"print-offers-custom-cmd-prompt",    F_CUSTOM_PRINT},
  1381.     {"print-includes-from-line",        F_FROM_DELIM_IN_PRINT},
  1382.     {"print-index-enabled",            F_PRINT_INDEX},
  1383.     {"print-formfeed-between-messages",    F_AGG_PRINT_FF},
  1384.     {"quell-dead-letter-on-cancel",        F_QUELL_DEAD_LETTER},
  1385.     {"quell-lock-failure-warnings",        F_QUELL_LOCK_FAILURE_MSGS},
  1386.     {"quell-status-message-beeping",    F_QUELL_BEEPS},
  1387.     {"quell-user-lookup-in-passwd-file",    F_QUELL_LOCAL_LOOKUP},
  1388.     {"quit-without-confirm",        F_QUIT_WO_CONFIRM},
  1389.     {"reply-always-uses-reply-to",        F_AUTO_REPLY_TO},
  1390.     {"save-aggregates-copy-sequence",    F_AGG_SEQ_COPY},
  1391.     {"save-will-quote-leading-froms",    F_QUOTE_ALL_FROMS},
  1392.     {"save-will-not-delete",        F_SAVE_WONT_DELETE},
  1393.     {"save-will-advance",            F_SAVE_ADVANCES},
  1394.     {"select-without-confirm",        F_SELECT_WO_CONFIRM},
  1395.     {"show-cursor",                F_SHOW_CURSOR},
  1396.     {"show-selected-in-boldface",        F_SELECTED_SHOWN_BOLD},
  1397.     {"signature-at-bottom",            F_SIG_AT_BOTTOM},
  1398.     {"single-column-folder-list",        F_VERT_FOLDER_LIST},
  1399.     {"tab-visits-next-new-message-only",    F_TAB_TO_NEW},
  1400.     {"use-current-dir",            F_USE_CURRENT_DIR},
  1401.     {"use-function-keys",            F_USE_FK},
  1402.     {"use-sender-not-x-sender",        F_USE_SENDER_NOT_X},
  1403.     {"use-subshell-for-suspend",        F_SUSPEND_SPAWNS}
  1404.     };
  1405.  
  1406.     return((index >= 0 && index < (sizeof(feat_list)/sizeof(feat_list[0])))
  1407.        ? &feat_list[index] : NULL);
  1408. }
  1409.  
  1410.  
  1411. /*
  1412.  * All the arguments past "list" are the backwards compatibility hacks.
  1413.  */
  1414. void
  1415. process_feature_list(ps, list, old_growth, hir, osr)
  1416.     struct pine *ps;
  1417.     char **list;
  1418.     int old_growth, hir, osr;
  1419. {
  1420.     register struct variable *vars = ps->vars;
  1421.     register char            *q;
  1422.     char                    **p,
  1423.                              *lvalue[LARGEST_BITMAP];
  1424.     int                       i,
  1425.                               yorn;
  1426.     NAMEVAL_S             *feat;
  1427.  
  1428.  
  1429.     /* backwards compatibility */
  1430.     if(hir)
  1431.     F_TURN_ON(F_INCLUDE_HEADER, ps);
  1432.  
  1433.     /* ditto */
  1434.     if(osr)
  1435.     F_TURN_ON(F_SIG_AT_BOTTOM, ps);
  1436.  
  1437.     /* ditto */
  1438.     if(old_growth)
  1439.         set_old_growth_bits(ps, 0);
  1440.  
  1441.     /* now run through the list (global, user, and cmd_line lists are here) */
  1442.     if(list){
  1443.       for(p = list; (q = *p) != NULL; p++){
  1444.     if(struncmp(q, "no-", 3) == 0){
  1445.       yorn = 0;
  1446.       q += 3;
  1447.     }else{
  1448.       yorn = 1;
  1449.     }
  1450.  
  1451.     for(i = 0; (feat = feature_list(i)) != NULL; i++){
  1452.       if(strucmp(q, feat->name) == 0){
  1453.         if(feat->value == F_OLD_GROWTH){
  1454.           set_old_growth_bits(ps, yorn);
  1455.         }else{
  1456.           F_SET(feat->value, ps, yorn);
  1457.         }
  1458.         break;
  1459.       }
  1460.     }
  1461.     /* if it wasn't in that list */
  1462.     if(feat == NULL)
  1463.           dprint(1, (debugfile,"Unrecognized feature in feature-list (%s%s)\n",
  1464.              (yorn ? "" : "no-"), q));
  1465.       }
  1466.     }
  1467.  
  1468.     /*
  1469.      * Turn on gratuitous '>From ' quoting, if requested...
  1470.      */
  1471.     mail_parameters(NULL, SET_FROMWIDGET,
  1472.             (void  *)(F_ON(F_QUOTE_ALL_FROMS, ps) ? 1 : 0));
  1473.  
  1474.     /*
  1475.      * Turn off .lock creation complaints...
  1476.      */
  1477.     if(F_ON(F_QUELL_LOCK_FAILURE_MSGS, ps))
  1478.       mail_parameters(NULL, SET_LOCKEACCESERROR, (void *) 0);
  1479.  
  1480.     if(F_ON(F_USE_FK, ps))
  1481.       ps->orig_use_fkeys = 1;
  1482.  
  1483.     /* Will we have to build a new list? */
  1484.     if(!(old_growth || hir || osr))
  1485.     return;
  1486.  
  1487.     /*
  1488.      * Build a new list for feature-list.  The only reason we ever need to
  1489.      * do this is if one of the obsolete options is being converted
  1490.      * into a feature-list item, and it isn't already included in the user's
  1491.      * feature-list.
  1492.      */
  1493.     i = 0;
  1494.     for(p = USR_FEATURE_LIST; p && (q = *p); p++){
  1495.       /* already have it or cancelled it, don't need to add later */
  1496.       if(hir && (strucmp(q, "include-header-in-reply") == 0 ||
  1497.                              strucmp(q, "no-include-header-in-reply") == 0)){
  1498.     hir = 0;
  1499.       }else if(osr && (strucmp(q, "signature-at-bottom") == 0 ||
  1500.                              strucmp(q, "no-signature-at-bottom") == 0)){
  1501.     osr = 0;
  1502.       }else if(old_growth && (strucmp(q, "old-growth") == 0 ||
  1503.                              strucmp(q, "no-old-growth") == 0)){
  1504.     old_growth = 0;
  1505.       }
  1506.       lvalue[i++] = cpystr(q);
  1507.     }
  1508.  
  1509.     /* check to see if we still need to build a new list */
  1510.     if(!(old_growth || hir || osr))
  1511.     return;
  1512.  
  1513.     if(hir)
  1514.       lvalue[i++] = "include-header-in-reply";
  1515.     if(osr)
  1516.       lvalue[i++] = "signature-at-bottom";
  1517.     if(old_growth)
  1518.       lvalue[i++] = "old-growth";
  1519.     lvalue[i] = NULL;
  1520.     set_variable_list(V_FEATURE_LIST, lvalue);
  1521. }
  1522.  
  1523.  
  1524.  
  1525. /*
  1526.  * set_old_growth_bits - Command used to set or unset old growth set
  1527.  *             of features
  1528.  */
  1529. void
  1530. set_old_growth_bits(ps, val)
  1531.     struct pine *ps;
  1532.     int          val;
  1533. {
  1534.     int i;
  1535.  
  1536.     for(i = 1; i <= F_LAST_FEATURE; i++)
  1537.       if(test_old_growth_bits(ps, i))
  1538.     F_SET(i, ps, val);
  1539. }
  1540.  
  1541.  
  1542.  
  1543. /*
  1544.  * test_old_growth_bits - Test to see if all the old growth bits are on,
  1545.  *              *or* if a particular feature index is in the old
  1546.  *              growth set.
  1547.  *
  1548.  * WEIRD ALERT: if index == F_OLD_GROWTH bit values are tested
  1549.  *              otherwise a bits existence in the set is tested!!!
  1550.  *
  1551.  * BUG: this will break if an old growth feature number is ever >= 32.
  1552.  */
  1553. int
  1554. test_old_growth_bits(ps, index)
  1555.     struct pine *ps;
  1556.     int          index;
  1557. {
  1558.     /*
  1559.      * this list defines F_OLD_GROWTH set
  1560.      */
  1561.     static unsigned long old_growth_bits = ((1 << F_ENABLE_FULL_HDR)     |
  1562.                         (1 << F_ENABLE_PIPE)         |
  1563.                         (1 << F_ENABLE_TAB_COMPLETE) |
  1564.                         (1 << F_QUIT_WO_CONFIRM)     |
  1565.                         (1 << F_ENABLE_JUMP)         |
  1566.                         (1 << F_ENABLE_ALT_ED)       |
  1567.                         (1 << F_ENABLE_BOUNCE)       |
  1568.                         (1 << F_ENABLE_AGG_OPS)     |
  1569.                         (1 << F_ENABLE_FLAG)         |
  1570.                         (1 << F_CAN_SUSPEND));
  1571.     if(index >= 32)
  1572.     return(0);
  1573.  
  1574.     if(index == F_OLD_GROWTH){
  1575.     for(index = 1; index <= F_LAST_FEATURE; index++)
  1576.       if(((1 << index) & old_growth_bits) && F_OFF(index, ps))
  1577.         return(0);
  1578.  
  1579.     return(1);
  1580.     }
  1581.     else
  1582.       return((1 << index) & old_growth_bits);
  1583. }
  1584.  
  1585.  
  1586.  
  1587. /*
  1588.  * Standard way to get at save message rules...
  1589.  */
  1590. NAMEVAL_S *
  1591. save_msg_rules(index)
  1592.     int index;
  1593. {
  1594.     static NAMEVAL_S save_rules[] = {
  1595.     {"by-from",                MSG_RULE_FROM},
  1596.     {"by-nick-of-from",            MSG_RULE_NICK_FROM_DEF},
  1597.     {"by-nick-of-from-then-from",        MSG_RULE_NICK_FROM},
  1598.     {"by-fcc-of-from",            MSG_RULE_FCC_FROM_DEF},
  1599.     {"by-fcc-of-from-then-from",        MSG_RULE_FCC_FROM},
  1600.     {"by-sender",                MSG_RULE_SENDER},
  1601.     {"by-nick-of-sender",            MSG_RULE_NICK_SENDER_DEF},
  1602.     {"by-nick-of-sender-then-sender",    MSG_RULE_NICK_SENDER},
  1603.     {"by-fcc-of-sender",            MSG_RULE_FCC_SENDER_DEF},
  1604.     {"by-fcc-of-sender-then-sender",    MSG_RULE_FCC_SENDER},
  1605.     {"by-recipient",            MSG_RULE_RECIP},
  1606.     {"by-nick-of-recip",            MSG_RULE_NICK_RECIP_DEF},
  1607.     {"by-nick-of-recip-then-recip",        MSG_RULE_NICK_RECIP},
  1608.     {"by-fcc-of-recip",            MSG_RULE_FCC_RECIP_DEF},
  1609.     {"by-fcc-of-recip-then-recip",        MSG_RULE_FCC_RECIP},
  1610.     {"last-folder-used",            MSG_RULE_LAST}, 
  1611.     {"default-folder",            MSG_RULE_DEFLT}
  1612.     };
  1613.  
  1614.     return((index >= 0 && index < (sizeof(save_rules)/sizeof(save_rules[0])))
  1615.        ? &save_rules[index] : NULL);
  1616. }
  1617.  
  1618.  
  1619. /*
  1620.  * Standard way to get at fcc rules...
  1621.  */
  1622. NAMEVAL_S *
  1623. fcc_rules(index)
  1624.     int index;
  1625. {
  1626.     static NAMEVAL_S f_rules[] = {
  1627.     {"default-fcc",        FCC_RULE_DEFLT}, 
  1628.     {"last-fcc-used",      FCC_RULE_LAST}, 
  1629.     {"by-recipient",       FCC_RULE_RECIP},
  1630.     {"by-nickname",        FCC_RULE_NICK},
  1631.     {"by-nick-then-recip", FCC_RULE_NICK_RECIP},
  1632.     {"current-folder",     FCC_RULE_CURRENT}
  1633.     };
  1634.  
  1635.     return((index >= 0 && index < (sizeof(f_rules)/sizeof(f_rules[0])))
  1636.        ? &f_rules[index] : NULL);
  1637. }
  1638.  
  1639.  
  1640. /*
  1641.  * Standard way to get at addrbook sort rules...
  1642.  */
  1643. NAMEVAL_S *
  1644. ab_sort_rules(index)
  1645.     int index;
  1646. {
  1647.     static NAMEVAL_S ab_rules[] = {
  1648.     {"fullname-with-lists-last",  AB_SORT_RULE_FULL_LISTS},
  1649.     {"fullname",                  AB_SORT_RULE_FULL}, 
  1650.     {"nickname-with-lists-last",  AB_SORT_RULE_NICK_LISTS},
  1651.     {"nickname",                  AB_SORT_RULE_NICK},
  1652.     {"dont-sort",                 AB_SORT_RULE_NONE}
  1653.     };
  1654.  
  1655.     return((index >= 0 && index < (sizeof(ab_rules)/sizeof(ab_rules[0])))
  1656.        ? &ab_rules[index] : NULL);
  1657. }
  1658.  
  1659.  
  1660. /*
  1661.  * Standard way to get at goto default rules...
  1662.  */
  1663. NAMEVAL_S *
  1664. goto_rules(index)
  1665.     int index;
  1666. {
  1667.     static NAMEVAL_S g_rules[] = {
  1668.     {"inbox-or-folder-in-recent-collection", GOTO_INBOX_RECENT_CLCTN},
  1669.     {"inbox-or-folder-in-first-collection",     GOTO_INBOX_FIRST_CLCTN},
  1670.     {"most-recent-folder",             GOTO_LAST_FLDR}
  1671.     };
  1672.  
  1673.     return((index >= 0 && index < (sizeof(g_rules)/sizeof(g_rules[0])))
  1674.        ? &g_rules[index] : NULL);
  1675. }
  1676.  
  1677.  
  1678. /*
  1679.  * The argument is an argument from the command line.  We check to see
  1680.  * if it is specifying an alternate value for one of the options that is
  1681.  * normally set in pinerc.  If so, we return 1 and set the appropriate
  1682.  * values in the variables array.
  1683.  */
  1684. int
  1685. pinerc_cmdline_opt(arg)
  1686.     char *arg;
  1687. {
  1688.     struct variable *v;
  1689.     char            *p,
  1690.                     *p1,
  1691.                     *value,
  1692.                    **lvalue;
  1693.     int              i;
  1694.  
  1695.     if(!arg || !arg[0])
  1696.       return 0;
  1697.  
  1698.     for(v = variables; v->name != NULL; v++){
  1699.       if(v->is_used && strncmp(v->name, arg, strlen(v->name)) == 0)
  1700.         break;
  1701.     }
  1702.  
  1703.     /* no match */
  1704.     if(v->name == NULL)
  1705.       return 0;
  1706.  
  1707.     if(v->is_obsolete || !v->is_user){
  1708.       fprintf(stderr, "Option \"%s\" is %s\n", v->name, v->is_obsolete ?
  1709.                        "obsolete" :
  1710.                        "not user settable");
  1711.       exit(-1);
  1712.     }
  1713.  
  1714.     /*----- Skip to '=' -----*/
  1715.     p1 = arg + strlen(v->name);
  1716.     while(*p1 && (*p1 == '\t' || *p1 == ' '))
  1717.       p1++;
  1718.  
  1719.     if(*p1 != '='){
  1720.       fprintf(stderr, "Missing \"=\" after -%s\n", v->name);
  1721.       exit(-1);
  1722.     }
  1723.  
  1724.     /* free mem */
  1725.     if(v->is_list){
  1726.       if(v->cmdline_val.l){
  1727.         char **p;
  1728.         for(p = v->cmdline_val.l; *p ; p++)
  1729.           fs_give((void **)p);
  1730.         fs_give((void **)&(v->cmdline_val.l));
  1731.       }
  1732.     }else{
  1733.       if(v->cmdline_val.p)
  1734.         fs_give((void **) &(v->cmdline_val.p));
  1735.     }
  1736.  
  1737.     p1++;
  1738.  
  1739.     /*----- Matched a variable, get its value ----*/
  1740.     while(*p1 == ' ' || *p1 == '\t')
  1741.       p1++;
  1742.     value = p1;
  1743.  
  1744.     if(*value == '\0'){
  1745.       if(v->is_list){
  1746.         v->cmdline_val.l = (char **)fs_get(2 * sizeof(char *));
  1747.     /*
  1748.      * we let people leave off the quotes on command line so that
  1749.      * they don't have to mess with shell quoting
  1750.      */
  1751.         v->cmdline_val.l[0] = cpystr("");
  1752.         v->cmdline_val.l[1] = NULL;
  1753.       }else{
  1754.         v->cmdline_val.p = cpystr("");
  1755.       }
  1756.       return 1;
  1757.     }
  1758.  
  1759.     /*--value is non-empty--*/
  1760.     if(*value == '"' && !v->is_list){
  1761.       value++;
  1762.       for(p1 = value; *p1 && *p1 != '"'; p1++);
  1763.         if(*p1 == '"')
  1764.           *p1 = '\0';
  1765.         else
  1766.           removing_trailing_white_space(value);
  1767.     }else{
  1768.       removing_trailing_white_space(value);
  1769.     }
  1770.  
  1771.     if(v->is_list){
  1772.       int   was_quoted = 0,
  1773.             count      = 1;
  1774.       char *error      = NULL;
  1775.  
  1776.       for(p1=value; *p1; p1++){        /* generous count of list elements */
  1777.         if(*p1 == '"')            /* ignore ',' if quoted   */
  1778.           was_quoted = (was_quoted) ? 0 : 1;
  1779.  
  1780.         if(*p1 == ',' && !was_quoted)
  1781.           count++;
  1782.       }
  1783.  
  1784.       lvalue = parse_list(value, count, &error);
  1785.       if(error){
  1786.     fprintf(stderr, "%s in %s = \"%s\"\n", error, v->name, value);
  1787.     exit(-1);
  1788.       }
  1789.       /*
  1790.        * Special case: turn "" strings into empty strings.
  1791.        * This allows users to turn off default lists.  For example,
  1792.        * if smtp-server is set then a user could override smtp-server
  1793.        * with smtp-server="".
  1794.        */
  1795.       for(i = 0; lvalue[i]; i++)
  1796.     if(lvalue[i][0] == '"' &&
  1797.        lvalue[i][1] == '"' &&
  1798.        lvalue[i][2] == '\0')
  1799.          lvalue[i][0] = '\0';
  1800.     }
  1801.  
  1802.     if(v->is_list)
  1803.       v->cmdline_val.l = lvalue;
  1804.     else
  1805.       v->cmdline_val.p = cpystr(value);
  1806.  
  1807.     return 1;
  1808. }
  1809.  
  1810.  
  1811. /*
  1812.  * Process the command list, changing function key notation into
  1813.  * lexical equivalents.
  1814.  */
  1815. void
  1816. process_init_cmds(ps, list)
  1817.     struct pine *ps;
  1818.     char **list;
  1819. {
  1820.     char **p;
  1821.     int i = 0;
  1822.     int j;
  1823. #define MAX_INIT_CMDS 500
  1824.     /* this is just a temporary stack array, the real one is allocated below */
  1825.     int i_cmds[MAX_INIT_CMDS];
  1826.     int fkeys = 0;
  1827.     int not_fkeys = 0;
  1828.   
  1829.     if(list){
  1830.       for(p = list; *p && i < MAX_INIT_CMDS; p++){
  1831.  
  1832.     /* regular character commands */
  1833.     if(strlen(*p) == 1){
  1834.  
  1835.       i_cmds[i++] = **p;
  1836.       not_fkeys++;
  1837.  
  1838.     }else if(strucmp(*p, "SPACE") == 0){
  1839.         i_cmds[i++] = ' ';
  1840.         not_fkeys++;
  1841.  
  1842.     }else if(strucmp(*p, "CR") == 0){
  1843.         i_cmds[i++] = '\n';
  1844.         not_fkeys++;
  1845.  
  1846.     }else if(strucmp(*p, "TAB") == 0){
  1847.         i_cmds[i++] = '\t';
  1848.         not_fkeys++;
  1849.  
  1850.     /* control chars */
  1851.     }else if(strlen(*p) == 2 && **p == '^'){
  1852.  
  1853.         i_cmds[i++] = ctrl(*((*p)+1));
  1854.  
  1855.     /* function keys */
  1856.     }else{
  1857.  
  1858.       fkeys++;
  1859.  
  1860.       if(**p == 'F' || **p == 'f'){
  1861.         int v;
  1862.  
  1863.         v = atoi((*p)+1);
  1864.         if(v >= 1 && v <= 12)
  1865.           i_cmds[i++] = PF1 + v - 1;
  1866.         else
  1867.           i_cmds[i++] = KEY_JUNK;
  1868.  
  1869.       }else if(strucmp(*p, "UP") == 0){
  1870.         i_cmds[i++] = KEY_UP;
  1871.       }else if(strucmp(*p, "DOWN") == 0){
  1872.         i_cmds[i++] = KEY_DOWN;
  1873.       }else if(strucmp(*p, "LEFT") == 0){
  1874.         i_cmds[i++] = KEY_LEFT;
  1875.       }else if(strucmp(*p, "RIGHT") == 0){
  1876.         i_cmds[i++] = KEY_RIGHT;
  1877.       }else{
  1878.         fkeys--;
  1879.         sprintf(tmp_20k_buf,
  1880.             "Bad initial keystroke \"%s\" (missing comma?)\n", *p);
  1881.         init_error(ps, tmp_20k_buf);
  1882.       }
  1883.     }
  1884.       }
  1885.     }
  1886.  
  1887.     /*
  1888.      * We don't handle the case where function keys are used to specify the
  1889.      * commands but some non-function key input is also required.  For example,
  1890.      * you might want to jump to a specific message number and view it
  1891.      * on start up.  To do that, you need to use character commands instead
  1892.      * of function key commands in the initial-keystroke-list.
  1893.      */
  1894.     if(fkeys && not_fkeys){
  1895.     init_error(ps,
  1896. "Mixed characters and function keys in \"initial-keystroke-list\", skipping!");
  1897.     i = 0;
  1898.     }
  1899.  
  1900.     if(fkeys && !not_fkeys)
  1901.       F_TURN_ON(F_USE_FK,ps);
  1902.     if(!fkeys && not_fkeys)
  1903.       F_TURN_OFF(F_USE_FK,ps);
  1904.  
  1905.     ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
  1906.     ps->free_initial_cmds = ps->initial_cmds;
  1907.     for(j = 0; j < i; j++)
  1908.     ps->initial_cmds[j] = i_cmds[j];
  1909.     ps->initial_cmds[i] = 0;
  1910.     if(i)
  1911.       ps->in_init_seq = ps->save_in_init_seq = 1;
  1912. }
  1913.  
  1914.  
  1915. /*
  1916.  * Choose from the global default, command line args, pinerc values to set
  1917.  * the actual value of the variable that we will use.  Start at the top
  1918.  * and work down from higher to lower precedence.
  1919.  */
  1920. void    
  1921. set_current_val(var, expand, cmdline)
  1922.     struct variable *var;
  1923.     int              expand, cmdline;
  1924. {
  1925.     int    is_set[4];  /* to see if we should warn user about fixed var */
  1926.     char **tmp;
  1927.  
  1928.     dprint(9, (debugfile,
  1929.            "set_current_val(var num=%d, expand=%d, cmdline=%d)\n",
  1930.            var - ps_global->vars, expand, cmdline));
  1931.  
  1932.     if(var->is_list){  /* variable is a list */
  1933.     char **list = NULL;
  1934.     int    i, j;
  1935.  
  1936.     dprint(9, (debugfile, "is_list: name=%s\n", var->name));
  1937.  
  1938.     /*
  1939.      * It's possible for non-null list elements to expand to
  1940.      * nothing, so we have to be a little more intelligent about
  1941.      * pre-expanding the lists to make sure such lists are
  1942.      * treated like empty lists...
  1943.      */
  1944.     for(j = 0; j < 4; j++){
  1945.         char **t;
  1946.  
  1947.         /*
  1948.          * The first one that's set wins.
  1949.          */
  1950.         t = j==0 ? var->fixed_val.l :
  1951.         j==1 ? (cmdline) ? var->cmdline_val.l : NULL :
  1952.         j==2 ? var->user_val.l :
  1953.                var->global_val.l;
  1954.  
  1955.         is_set[j] = 0;
  1956.  
  1957.         if(t){
  1958.         if(!expand){
  1959.             is_set[j]++;
  1960.             if(!list)
  1961.               list = t;
  1962.         }
  1963.         else{
  1964.             for(i = 0; t[i]; i++){
  1965.             if(expand_variables(tmp_20k_buf, t[i])){
  1966.                 /* successful expand */
  1967.                 is_set[j]++;
  1968.                 if(!list)
  1969.                   list = t;
  1970.  
  1971.                 break;
  1972.             }
  1973.             }
  1974.         }
  1975.         }
  1976.     }
  1977.  
  1978.     /* Admin wants default, which is global_val. */
  1979.     if(var->is_fixed && var->fixed_val.l == NULL)
  1980.       list = var->global_val.l;
  1981.             
  1982.     if(var->current_val.l){     /* clean up any old values */
  1983.         for(tmp = var->current_val.l; *tmp ; tmp++)
  1984.           fs_give((void **)tmp);
  1985.  
  1986.         fs_give((void **)&var->current_val.l);
  1987.     }
  1988.  
  1989.     if(list){
  1990.         int cnt = 0;
  1991.  
  1992.         for(; list[cnt]; cnt++)    /* item count */
  1993.           ;/* do nothing */
  1994.  
  1995.         dprint(9, (debugfile, "counted %d items\n", cnt));
  1996.  
  1997.         var->current_val.l = (char **)fs_get((cnt+1)*sizeof(char *));
  1998.         tmp = var->current_val.l;
  1999.  
  2000.         for(i = 0; list[i]; i++){
  2001.         if(!expand)
  2002.           *tmp++ = cpystr(list[i]);
  2003.         else if(expand_variables(tmp_20k_buf, list[i]))
  2004.           *tmp++ = cpystr(tmp_20k_buf);
  2005.         }
  2006.  
  2007.         *tmp = NULL;
  2008.     }
  2009.     else
  2010.       var->current_val.l = NULL;
  2011.  
  2012.     }
  2013.  
  2014.     else{  /* variable is not a list */
  2015.     char *strvar = NULL;
  2016.     char *t;
  2017.     int   j;
  2018.  
  2019.     for(j = 0; j < 4; j++){
  2020.  
  2021.         t = j==0 ? var->fixed_val.p :
  2022.         j==1 ? (cmdline) ? var->cmdline_val.p : NULL :
  2023.         j==2 ? var->user_val.p :
  2024.                var->global_val.p;
  2025.  
  2026.         is_set[j] = 0;
  2027.  
  2028.         if(t){
  2029.         if(!expand){
  2030.             is_set[j]++;
  2031.             if(!strvar)
  2032.             strvar = t;
  2033.         }
  2034.         else if(expand_variables(tmp_20k_buf, t)){
  2035.             is_set[j]++;
  2036.             if(!strvar)
  2037.             strvar = t;
  2038.         }
  2039.         }
  2040.     }
  2041.  
  2042.     /* Admin wants default, which is global_val. */
  2043.     if(var->is_fixed && var->fixed_val.p == NULL)
  2044.       strvar = var->global_val.p;
  2045.  
  2046.     if(var->current_val.p)        /* free previous value */
  2047.       fs_give((void **)&var->current_val.p);
  2048.  
  2049.     if(strvar){
  2050.         if(!expand)
  2051.           var->current_val.p = cpystr(strvar);
  2052.         else{
  2053.         expand_variables(tmp_20k_buf, strvar);
  2054.         var->current_val.p = cpystr(tmp_20k_buf);
  2055.         }
  2056.     }
  2057.     else
  2058.       var->current_val.p = NULL;
  2059.     }
  2060.  
  2061.     if(var->is_fixed){
  2062.     char **list;
  2063.     int i, j, fixed_len, user_len;
  2064.  
  2065.     /*
  2066.      * sys mgr fixed this variable and user is trying to change it
  2067.      */
  2068.     if(is_set[2]){
  2069.         if(var->is_list){
  2070.         /* If same length and same contents, don't warn. */
  2071.         for(list=var->fixed_val.l; list && *list; list++)
  2072.           ;/* just counting */
  2073.  
  2074.         fixed_len = var->fixed_val.l ? (list - var->fixed_val.l) : 0;
  2075.         for(list=var->user_val.l; list && *list; list++)
  2076.           ;/* just counting */
  2077.  
  2078.         user_len = var->user_val.l ? (list - var->user_val.l) : 0;
  2079.         if(user_len == fixed_len){
  2080.           for(i=0; i < user_len; i++){
  2081.             for(j=0; j < user_len; j++)
  2082.               if(!strucmp(var->user_val.l[i], var->fixed_val.l[j]))
  2083.             break;
  2084.               
  2085.             if(j == user_len){
  2086.               ps_global->give_fixed_warning = 1;
  2087.               ps_global->fix_fixed_warning = 1;
  2088.               break;
  2089.             }
  2090.           }
  2091.         }
  2092.         else{
  2093.             ps_global->give_fixed_warning = 1;
  2094.             ps_global->fix_fixed_warning = 1;
  2095.         }
  2096.         }
  2097.         else if(var->fixed_val.p && !var->user_val.p
  2098.             || !var->fixed_val.p && var->user_val.p
  2099.             || (var->fixed_val.p && var->user_val.p
  2100.                 && strucmp(var->fixed_val.p, var->user_val.p))){
  2101.         ps_global->give_fixed_warning = 1;
  2102.         ps_global->fix_fixed_warning = 1;
  2103.         }
  2104.     }
  2105.  
  2106.     if(is_set[1]){
  2107.         if(var->is_list){
  2108.         /* If same length and same contents, don't warn. */
  2109.         for(list=var->fixed_val.l; list && *list; list++)
  2110.           ;/* just counting */
  2111.  
  2112.         fixed_len = var->fixed_val.l ? (list - var->fixed_val.l) : 0;
  2113.         for(list=var->cmdline_val.l; list && *list; list++)
  2114.           ;/* just counting */
  2115.  
  2116.         user_len = var->cmdline_val.l ? (list - var->cmdline_val.l) : 0;
  2117.         if(user_len == fixed_len){
  2118.           for(i=0; i < user_len; i++){
  2119.             for(j=0; j < user_len; j++)
  2120.               if(!strucmp(var->cmdline_val.l[i], var->fixed_val.l[j]))
  2121.             break;
  2122.               
  2123.             if(j == user_len){
  2124.               ps_global->give_fixed_warning = 1;
  2125.               break;
  2126.             }
  2127.           }
  2128.         }
  2129.         else{
  2130.             ps_global->give_fixed_warning = 1;
  2131.         }
  2132.         }
  2133.         else if(var->fixed_val.p && !var->cmdline_val.p
  2134.             || !var->fixed_val.p && var->cmdline_val.p
  2135.             || (var->fixed_val.p && var->cmdline_val.p
  2136.                 && strucmp(var->fixed_val.p, var->cmdline_val.p))){
  2137.         ps_global->give_fixed_warning = 1;
  2138.         }
  2139.     }
  2140.     }
  2141. }
  2142.  
  2143.  
  2144. /*
  2145.  * Feature-list has to be handled separately from the other variables
  2146.  * because it is additive.  The other variables choose one of command line,
  2147.  * or pine.conf, or pinerc.  Feature list adds them.  This could easily be
  2148.  * converted to a general purpose routine if we add more additive variables.
  2149.  *
  2150.  * This works by replacing earlier values with later ones.  That is, command
  2151.  * line settings have higher precedence than global settings and that is
  2152.  * accomplished by putting the command line features after the global
  2153.  * features in the list.  When they are processed, the last one wins.
  2154.  *
  2155.  * Feature-list also has a backwards compatibility hack.
  2156.  */
  2157. void    
  2158. set_feature_list_current_val(var)
  2159.   struct variable *var;
  2160. {
  2161.     char **list;
  2162.     char **list_fixed;
  2163.     int    i, j, k,
  2164.        elems = 0;
  2165.  
  2166.     /* count the lists so I can allocate */
  2167.     if(list=var->global_val.l)
  2168.       for(i = 0; list[i]; i++)
  2169.         elems++;
  2170.     if(list=var->user_val.l)
  2171.       for(i = 0; list[i]; i++)
  2172.         elems++;
  2173.     if(list=var->cmdline_val.l)
  2174.       for(i = 0; list[i]; i++)
  2175.         elems++;
  2176.     if(list=ps_global->feat_list_back_compat)
  2177.       for(i = 0; list[i]; i++)
  2178.         elems++;
  2179.     if(list=var->fixed_val.l)
  2180.       for(i = 0; list[i]; i++)
  2181.         elems++;
  2182.  
  2183.     list_fixed = var->fixed_val.l;
  2184.  
  2185.     var->current_val.l = (char **)fs_get((elems+1) * sizeof(char *));
  2186.  
  2187.     j = 0;
  2188.     if(list=var->global_val.l)
  2189.       for(i = 0; list[i]; i++)
  2190.         var->current_val.l[j++] = cpystr(list[i]);
  2191.     /*
  2192.      * We need to warn the user if the sys mgr has restricted him or her
  2193.      * from changing a feature that he or she is trying to change.
  2194.      *
  2195.      * I'm not catching the old-growth macro since I'm just comparing
  2196.      * strings.  That is, it works correctly, but the user won't be warned
  2197.      * if the user old-growth and the mgr says no-quit-without-confirm.
  2198.      */
  2199.     if(list=var->user_val.l)
  2200.       for(i = 0; list[i]; i++){
  2201.         var->current_val.l[j++] = cpystr(list[i]);
  2202.         for(k = 0; list_fixed && list_fixed[k]; k++){
  2203.       char *p, *q;
  2204.       p = list[i];
  2205.       q = list_fixed[k];
  2206.       if(!struncmp(p, "no-", 3))
  2207.         p += 3;
  2208.       if(!struncmp(q, "no-", 3))
  2209.         q += 3;
  2210.       if(!strucmp(q, p) && strucmp(list[i], list_fixed[k])){
  2211.           ps_global->give_fixed_warning = 1;
  2212.           ps_global->fix_fixed_warning = 1;
  2213.       }
  2214.     }
  2215.       }
  2216.     if(list=var->cmdline_val.l)
  2217.       for(i = 0; list[i]; i++){
  2218.         var->current_val.l[j++] = cpystr(list[i]);
  2219.         for(k = 0; list_fixed && list_fixed[k]; k++){
  2220.       char *p, *q;
  2221.       p = list[i];
  2222.       q = list_fixed[k];
  2223.       if(!struncmp(p, "no-", 3))
  2224.         p += 3;
  2225.       if(!struncmp(q, "no-", 3))
  2226.         q += 3;
  2227.       if(!strucmp(q, p) && strucmp(list[i], list_fixed[k]))
  2228.         ps_global->give_fixed_warning = 1;
  2229.     }
  2230.       }
  2231.     if(list=var->fixed_val.l)
  2232.       for(i = 0; list[i]; i++)
  2233.         var->current_val.l[j++] = cpystr(list[i]);
  2234.     if(list=ps_global->feat_list_back_compat)
  2235.       for(i = 0; list[i]; i++)
  2236.         var->current_val.l[j++] = cpystr(list[i]);
  2237.  
  2238.     var->current_val.l[j] = NULL;
  2239. }
  2240.                                                      
  2241.  
  2242.  
  2243. /*----------------------------------------------------------------------
  2244.  
  2245.     Expand Metacharacters/variables in file-names
  2246.  
  2247.    Read input line and expand shell-variables/meta-characters
  2248.  
  2249.     <input>        <replaced by>
  2250.     ${variable}    getenv("variable")
  2251.     $variable    getenv("variable")
  2252.     ~        getenv("HOME")
  2253.     \c        c
  2254.     <others>    <just copied>
  2255.  
  2256. NOTE handling of braces in ${name} doesn't check much or do error recovery
  2257.     
  2258.   ----*/
  2259.  
  2260. char *
  2261. expand_variables(lineout, linein)
  2262. char *linein, *lineout;
  2263. {
  2264.     char *src = linein, *dest = lineout, *p;
  2265.     int  envexpand = 0;
  2266.  
  2267.     if(!linein)
  2268.       return(NULL);
  2269.  
  2270.     while( *src ){            /* something in input string */
  2271. #if defined(DOS) || defined(OS2)
  2272.         if(*src == '$' && *(src+1) == '$'){
  2273.         /*
  2274.          * backslash to escape chars we're interested in, else
  2275.          * it's up to the user of the variable to handle the 
  2276.          * backslash...
  2277.          */
  2278.             *dest++ = *++src;        /* copy next as is */
  2279.         }else
  2280. #else
  2281.         if(*src == '\\' && *(src+1) == '$'){
  2282.         /*
  2283.          * backslash to escape chars we're interested in, else
  2284.          * it's up to the user of the variable to handle the 
  2285.          * backslash...
  2286.          */
  2287.             *dest++ = *++src;        /* copy next as is */
  2288.         }else if(*src == '~' && src == linein){
  2289.         char buf[MAXPATH];
  2290.         int  i;
  2291.  
  2292.         for(i = 0; src[i] && src[i] != '/'; i++)
  2293.           buf[i] = src[i];
  2294.  
  2295.         src    += i;        /* advance src pointer */
  2296.         buf[i]  = '\0';        /* tie off buf string */
  2297.         fnexpand(buf, MAXPATH);    /* expand the path */
  2298.  
  2299.         for(p = buf; *dest = *p; p++, dest++)
  2300.           ;
  2301.  
  2302.         continue;
  2303.         }else
  2304. #endif
  2305.     if(*src == '$'){        /* shell variable */
  2306.         char word[128];
  2307.         int  found_brace = 0;
  2308.  
  2309.         envexpand++;        /* signal that we've expanded a var */
  2310.         src++;            /* skip dollar */
  2311.         p = word;
  2312.         if(*src == '{'){        /* starts with brace? */
  2313.         src++;        
  2314.         found_brace = 1;
  2315.         }
  2316.  
  2317.         while(*src && !isspace((unsigned char)*src)
  2318.           && (!found_brace || *src != '}'))
  2319.           *p++ = *src++;        /* copy to word */
  2320.  
  2321.         if(found_brace){    /* look for closing  brace */
  2322.         while(*src && *src != '}')
  2323.           src++;        /* skip until brace */
  2324.  
  2325.         if(*src == '}')    /* skip brace */
  2326.           src++;
  2327.         }
  2328.  
  2329.         *p = '\0';            /* tie off word */
  2330.  
  2331.         if(p = getenv(word))     /* check for word in environment */
  2332.           while(*p)
  2333.         *dest++ = *p++;
  2334.  
  2335.         continue;
  2336.     }else{                /* other cases: just copy */
  2337.         *dest++ = *src;
  2338.     }
  2339.  
  2340.         if(*src)            /* next character (if any) */
  2341.       src++;
  2342.     }
  2343.  
  2344.     *dest = '\0';
  2345.     return((envexpand && lineout[0] == '\0') ? NULL : lineout);
  2346. }
  2347.  
  2348.  
  2349. /*----------------------------------------------------------------------
  2350.     Sets  login, full_username and home_dir
  2351.  
  2352.    Args: ps -- The Pine structure to put the user name, etc in
  2353.  
  2354.   Result: sets the fullname, login and home_dir field of the pine structure
  2355.           returns 0 on success, -1 if not.
  2356.   ----*/
  2357. #define    MAX_INIT_ERRS    5
  2358. void
  2359. init_error(ps, s)
  2360.     struct pine *ps;
  2361.     char    *s;
  2362. {
  2363.     int    i;
  2364.  
  2365.     if(!ps->init_errs){
  2366.     ps->init_errs = (char **)fs_get((MAX_INIT_ERRS + 1) * sizeof(char *));
  2367.     memset(ps->init_errs, 0, (MAX_INIT_ERRS + 1) * sizeof(char *));
  2368.     }
  2369.  
  2370.     for(i = 0; i < MAX_INIT_ERRS; i++)
  2371.       if(!ps->init_errs[i]){
  2372.       ps->init_errs[i] = cpystr(s);
  2373.       dprint(2, (debugfile, "%s\n", s));
  2374.       break;
  2375.       }
  2376. }
  2377.  
  2378.  
  2379. /*----------------------------------------------------------------------
  2380.     Sets  login, full_username and home_dir
  2381.  
  2382.    Args: ps -- The Pine structure to put the user name, etc in
  2383.  
  2384.   Result: sets the fullname, login and home_dir field of the pine structure
  2385.           returns 0 on success, -1 if not.
  2386.   ----*/
  2387.  
  2388. init_username(ps)
  2389.      struct pine *ps;
  2390. {
  2391.     char fld_dir[MAXPATH+1], *expanded;
  2392.     int  rv;
  2393.  
  2394.     rv       = 0;
  2395.     expanded = NULL;
  2396. #if defined(DOS) || defined(OS2)
  2397.     if(ps->COM_USER_ID)
  2398.       expanded = expand_variables(tmp_20k_buf,
  2399.                   ps->COM_USER_ID);
  2400.     
  2401.     if(!expanded && ps->USR_USER_ID)
  2402.       expanded = expand_variables(tmp_20k_buf, ps->USR_USER_ID);
  2403.  
  2404.     if(!expanded)
  2405.       ps->blank_user_id = 1;
  2406.  
  2407.     ps->VAR_USER_ID = cpystr(expanded ? expanded : "");
  2408. #else
  2409.     ps->VAR_USER_ID = cpystr(ps->ui.login);
  2410.     if(!ps->VAR_USER_ID[0]){
  2411.         fprintf(stderr, "Who are you? (Unable to look up login name)\n");
  2412.         rv = -1;
  2413.     }
  2414. #endif
  2415.  
  2416.     expanded = NULL;
  2417.     if(ps->vars[V_PERSONAL_NAME].is_fixed){
  2418.     if(ps->FIX_PERSONAL_NAME){
  2419.             expanded = expand_variables(tmp_20k_buf, ps->FIX_PERSONAL_NAME);
  2420.     }
  2421.     if(ps->USR_PERSONAL_NAME){
  2422.         ps_global->give_fixed_warning = 1;
  2423.         ps_global->fix_fixed_warning = 1;
  2424.     }
  2425.     else if(ps->COM_PERSONAL_NAME)
  2426.       ps_global->give_fixed_warning = 1;
  2427.     }
  2428.     else{
  2429.     if(ps->COM_PERSONAL_NAME)
  2430.       expanded = expand_variables(tmp_20k_buf,
  2431.                 ps->COM_PERSONAL_NAME);
  2432.  
  2433.     if(!expanded && ps->USR_PERSONAL_NAME)
  2434.       expanded = expand_variables(tmp_20k_buf,
  2435.                       ps->USR_PERSONAL_NAME);
  2436.     }
  2437.  
  2438.     if(!expanded){
  2439.     expanded = ps->ui.fullname;
  2440. #if defined(DOS) || defined(OS2)
  2441.     ps->blank_personal_name = 1;
  2442. #endif
  2443.     }
  2444.  
  2445.     ps->VAR_PERSONAL_NAME = cpystr(expanded ? expanded : "");
  2446.  
  2447.     if(strlen(ps->home_dir) + strlen(ps->VAR_MAIL_DIRECTORY)+2 > MAXPATH){
  2448.         printf("Folders directory name is longer than %d\n", MAXPATH);
  2449.         printf("Directory name: \"%s/%s\"\n",ps->home_dir,
  2450.                ps->VAR_MAIL_DIRECTORY);
  2451.         return(-1);
  2452.     }
  2453. #if defined(DOS) || defined(OS2)
  2454.     if(ps->VAR_MAIL_DIRECTORY[1] == ':')
  2455.       strcpy(fld_dir, ps->VAR_MAIL_DIRECTORY);
  2456.     else
  2457. #endif
  2458.     build_path(fld_dir, ps->home_dir, ps->VAR_MAIL_DIRECTORY);
  2459.     ps->folders_dir = cpystr(fld_dir);
  2460.  
  2461.     dprint(1, (debugfile, "Userid: %s\nFullname: \"%s\"\n",
  2462.                ps->VAR_USER_ID, ps->VAR_PERSONAL_NAME));
  2463.     return(rv);
  2464. }
  2465.  
  2466.  
  2467. /*----------------------------------------------------------------------
  2468.         Fetch the hostname of the current system and put it in pine struct
  2469.  
  2470.    Args: ps -- The pine structure to put the hostname, etc in
  2471.  
  2472.   Result: hostname, localdomain, userdomain and maildomain are set
  2473.  
  2474.  
  2475. ** Pine uses the following set of names:
  2476.   hostname -    The fully-qualified hostname.  Obtained with
  2477.         gethostbyname() which reads /etc/hosts or does a DNS
  2478.         lookup.  This may be blank.
  2479.   localdomain - The domain name without the host.  Obtained from the
  2480.         above hostname if it has a "." in it.  Removes first
  2481.         segment.  If hostname has no "." in it then the hostname
  2482.         is used.  This may be blank.
  2483.   userdomain -  Explicitly configured domainname.  This is read out of the
  2484.         global pine.conf or user's .pinerc.  The user's entry in the
  2485.         .pinerc overrides.
  2486.  
  2487. ** Pine has the following uses for such names:
  2488.  
  2489.   1. On outgoing messages in the From: line
  2490.     Uses userdomain if there is one.  If not uses, uses
  2491.     hostname unless Pine has been configured to use localdomain.
  2492.  
  2493.   2. When expanding/fully-qualifying unqualified addresses during
  2494.      composition
  2495.     (same as 1)
  2496.  
  2497.   3. When expanding/fully-qualifying unqualified addresses during
  2498.      composition when a local entry in the password file exists for
  2499.      name.
  2500.         If no userdomain is given, then this lookup is always done
  2501.     and the hostname is used unless Pine's been configured to 
  2502.     use the localdomain.  If userdomain is defined, it is used,
  2503.     but no local lookup is done.  We can't assume users on the
  2504.     local host are valid in the given domain (and, for simplicity,
  2505.     have chosen to ignore the cases userdomain matches localdomain
  2506.     or localhost).  Setting user-lookup-even-if-domain-mismatch
  2507.     feature will tell pine to override this behavior and perform
  2508.     the local lookup anyway.  The problem of a global "even-if"
  2509.     set and a .pinerc-defined user-domain of something odd causing
  2510.     the local lookup, but this will only effect the personal name, 
  2511.     and is not judged to be a significant problem.
  2512.  
  2513.   4. In determining if an address is that of the current pine user for
  2514.      formatting index and filtering addresses when replying
  2515.     If a userdomain is specified the address must match the
  2516.     userdomain exactly.  If a userdomain is not specified or the
  2517.     userdomain is the same as the hostname or domainname, then
  2518.     an address will be considered the users if it matches either
  2519.     the domainname or the hostname.  Of course, the userid must
  2520.     match too. 
  2521.  
  2522.   5. In Message ID's
  2523.     The fully-qualified hostname is always users here.
  2524.  
  2525.  
  2526. ** Setting the domain names
  2527.   To set the domain name for all Pine users on the system to be
  2528. different from what Pine figures out from DNS, set the domain name in
  2529. the "user-domain" variable in pine.conf.  To set the domain name for an
  2530. individual user, set the "user-domain" variable in his .pinerc.
  2531. The .pinerc setting overrides any other setting.
  2532.  ----*/
  2533. init_hostname(ps)
  2534.      struct pine *ps;
  2535. {
  2536.     char hostname[MAX_ADDRESS+1], domainname[MAX_ADDRESS+1];
  2537.  
  2538.     getdomainnames(hostname, MAX_ADDRESS, domainname, MAX_ADDRESS);
  2539.  
  2540.     if(ps->hostname)
  2541.       fs_give((void **)&ps->hostname);
  2542.  
  2543.     ps->hostname = cpystr(hostname);
  2544.  
  2545.     if(ps->localdomain)
  2546.       fs_give((void **)&ps->localdomain);
  2547.  
  2548.     ps->localdomain = cpystr(domainname);
  2549.     ps->userdomain  = NULL;
  2550.  
  2551.     if(ps->VAR_USER_DOMAIN && ps->VAR_USER_DOMAIN[0]){
  2552.         ps->maildomain = ps->userdomain = ps->VAR_USER_DOMAIN;
  2553.     }else{
  2554. #if defined(DOS) || defined(OS2)
  2555.     if(ps->VAR_USER_DOMAIN)
  2556.       ps->blank_user_domain = 1;    /* user domain set to null string! */
  2557.  
  2558.         ps->maildomain = ps->localdomain[0] ? ps->localdomain : ps->hostname;
  2559. #else
  2560.         ps->maildomain = strucmp(ps->VAR_USE_ONLY_DOMAIN_NAME, "yes")
  2561.               ? ps->hostname : ps->localdomain;
  2562. #endif
  2563.     }
  2564.  
  2565.     /*
  2566.      * Tell c-client what domain to use when completing unqualified
  2567.      * addresses it finds in local mailboxes.  Remember, it won't 
  2568.      * affect what's to the right of '@' for unqualified addresses in
  2569.      * remote folders...
  2570.      */
  2571.     mail_parameters(NULL, SET_LOCALHOST, (void *) ps->maildomain);
  2572.     if(!strchr(ps->maildomain, '.')){
  2573.     sprintf(tmp_20k_buf, "Incomplete maildomain \"%s\".",
  2574.         ps->maildomain);
  2575.     init_error(ps, tmp_20k_buf);
  2576.     strcpy(tmp_20k_buf,
  2577.            "Return address in mail you send may be incorrect.");
  2578.     init_error(ps, tmp_20k_buf);
  2579.     }
  2580.  
  2581.     dprint(1, (debugfile,"User domain name being used \"%s\"\n",
  2582.                ps->userdomain == NULL ? "" : ps->userdomain));
  2583.     dprint(1, (debugfile,"Local Domain name being used \"%s\"\n",
  2584.                ps->localdomain));
  2585.     dprint(1, (debugfile,"Host name being used \"%s\"\n",
  2586.                ps->hostname));
  2587.     dprint(1, (debugfile,
  2588.            "Mail Domain name being used (by c-client too)\"%s\"\n",
  2589.                ps->maildomain));
  2590.  
  2591.     if(!ps->maildomain || !ps->maildomain[0]){
  2592. #if defined(DOS) || defined(OS2)
  2593.     if(ps->blank_user_domain)
  2594.       return(0);        /* prompt for this in send.c:dos_valid_from */
  2595. #endif
  2596.         fprintf(stderr, "No host name or domain name set\n");
  2597.         return(-1);
  2598.     }
  2599.     else
  2600.       return(0);
  2601. }
  2602.  
  2603.  
  2604. /*----------------------------------------------------------------------
  2605.          Read and parse a pinerc file
  2606.   
  2607.    Args:  Filename   -- name of the .pinerc file to open and read
  2608.           vars       -- The vars structure to store values in
  2609.           which_vars -- Whether the local or global values are being read
  2610.  
  2611.    Result: 
  2612.  
  2613.  This may be the local file or the global file.  The values found are
  2614. merged with the values currently in vars.  All values are strings and
  2615. are malloced; and existing values will be freed before the assignment.
  2616. Those that are <unset> will be left unset; their values will be NULL.
  2617.   ----*/
  2618. void
  2619. read_pinerc(filename, vars, which_vars)
  2620.      char *filename;
  2621.      ParsePinerc which_vars;
  2622.      struct variable *vars;
  2623. {
  2624.     char               *file, *value, **lvalue, *line, *error;
  2625.     register char      *p, *p1;
  2626.     struct variable    *v;
  2627.     struct pinerc_line *pline;
  2628.     int                 line_count, was_quoted;
  2629.     int            i;
  2630.  
  2631.     dprint(1, (debugfile, "reading_pinerc \"%s\"\n", filename));
  2632.  
  2633.     file = read_file(filename);
  2634.     if(file == NULL){
  2635.         dprint(1, (debugfile, "Open failed: %s\n", error_description(errno)));
  2636.     if(which_vars == ParseLocal)
  2637.         ps_global->first_time_user = 1;
  2638.         return;
  2639.     }
  2640.     else{
  2641.     /*
  2642.      * fixup unix newlines?  note: this isn't a problem under dos
  2643.      * since the file's read in text mode.
  2644.      */
  2645.     for(p = file; *p && *p != '\012'; p++)
  2646.       ;
  2647.  
  2648.     if(p > file && *p && *(p-1) == '\015')    /* cvt crlf to lf */
  2649.       for(p1 = p - 1; *p1 = *p; p++)
  2650.         if(!(*p == '\015' && *(p+1) == '\012'))
  2651.           p1++;
  2652.     }
  2653.  
  2654.     dprint(1, (debugfile, "Read %d characters:\n", strlen(file)));
  2655.  
  2656.     if(which_vars == ParseLocal){
  2657.       /*--- Count up lines and allocate structures */
  2658.       for(line_count = 0, p = file; *p != '\0'; p++)
  2659.         if(*p == '\n')
  2660.       line_count++;
  2661.       pinerc_lines = (struct pinerc_line *)
  2662.                fs_get((3 + line_count) * sizeof(struct pinerc_line));
  2663.       memset((void *)pinerc_lines, 0,
  2664.                 (3 + line_count) * sizeof(struct pinerc_line));
  2665.       pline = pinerc_lines;
  2666.  
  2667.       /* copyright stuff, sorry, magic numbers galore */
  2668.       p = file;
  2669.       if(which_vars == ParseLocal){
  2670.         if(strncmp(p, cf_text_comment1, strlen(cf_text_comment1)) == 0){
  2671.       /* find the comma after the version number */
  2672.       p = strchr(p+strlen(cf_text_comment1), ',');
  2673.           if(p && strncmp(p, cf_text_comment2, 11) == 0){
  2674.             p += strlen(cf_text_comment2) - 26;
  2675.             if(p &&
  2676.          strncmp(p, cf_text_comment2+strlen(cf_text_comment2)-26,26) == 0){
  2677.            copyright_line_is_there = 1;
  2678.         }
  2679.       }
  2680.         }
  2681.         p = strchr(p, '\n');
  2682.         if(p && *(p+1) && strncmp(p+1, cf_text_comment3, 27) == 0)
  2683.           trademark_lines_are_there = 1;
  2684.       }
  2685.     }
  2686.  
  2687.     for(p = file, line = file; *p != '\0';){
  2688.         /*----- Grab the line ----*/
  2689.         line = p;
  2690.         while(*p && *p != '\n')
  2691.           p++;
  2692.         if(*p == '\n'){
  2693.             *p++ = '\0';
  2694.         }
  2695.  
  2696.         /*----- Comment Line -----*/
  2697.         if(*line == '#'){
  2698.             if(which_vars == ParseLocal){
  2699.                 pline->is_var = 0;
  2700.                 pline->line = cpystr(line);
  2701.                 pline++;
  2702.             }
  2703.             continue;
  2704.         }
  2705.  
  2706.     if(*line == '\0' || *line == '\t' || *line == ' '){
  2707.             p1 = line;
  2708.             while(*p1 == '\t' || *p1 == ' ')
  2709.                p1++;
  2710.             if(which_vars == ParseLocal){
  2711.             if(*p1 != '\0') /* contin. line from some future version? */
  2712.                     pline->line = cpystr(line);
  2713.             else
  2714.                     pline->line = cpystr("");
  2715.                pline->is_var = 0;
  2716.                pline++;
  2717.             }
  2718.             continue;
  2719.     }
  2720.  
  2721.         /*----- look up matching 'v' and leave "value" after '=' ----*/
  2722.         for(v = vars; *line && v->name; v++)
  2723.       if((i = strlen(v->name)) < strlen(line) && !strncmp(v->name,line,i)){
  2724.           int j;
  2725.  
  2726.           for(j = i; line[j] == ' ' || line[j] == '\t'; j++)
  2727.         ;
  2728.  
  2729.           if(line[j] == '='){    /* bingo! */
  2730.           for(value = &line[j+1];
  2731.               *value == ' ' || *value == '\t';
  2732.               value++)
  2733.             ;
  2734.  
  2735.           break;
  2736.           }
  2737.           /* else either unrecognized var or bogus line */
  2738.       }
  2739.  
  2740.         /*----- Didn't match any variable or bogus format -----*/
  2741.         if(!v->name){
  2742.             if(which_vars == ParseLocal){
  2743.                 pline->is_var = 0;
  2744.                 pline->line = cpystr(line);
  2745.                 pline++;
  2746.             }
  2747.             continue;
  2748.         }
  2749.  
  2750.         /*----- Obsolete variable, read it anyway below, might use it -----*/
  2751.         if(v->is_obsolete){
  2752.             if(which_vars == ParseLocal){
  2753.                 pline->obsolete_var = 1;
  2754.                 pline->line = cpystr(line);
  2755.                 pline->var = v;
  2756.             }
  2757.         }
  2758.  
  2759.         /*----- Variable is in the list but unused for some reason -----*/
  2760.         if(!v->is_used){
  2761.             if(which_vars == ParseLocal){
  2762.                 pline->is_var = 0;
  2763.                 pline->line = cpystr(line);
  2764.                 pline++;
  2765.             }
  2766.             continue;
  2767.         }
  2768.  
  2769.         /*--- Var is not user controlled, leave it alone for back compat ---*/
  2770.         if(!v->is_user && which_vars == ParseLocal){
  2771.         pline->is_var = 0;
  2772.         pline->line = cpystr(line);
  2773.         pline++;
  2774.         continue;
  2775.         }
  2776.  
  2777.     if(which_vars == ParseFixed)
  2778.         v->is_fixed = 1;
  2779.  
  2780.         /*---- variable is unset, or it global but expands to nothing ----*/
  2781.         if(!*value
  2782.        || (which_vars == ParseGlobal
  2783.            && !expand_variables(tmp_20k_buf, value))){
  2784.             if(v->is_user && which_vars == ParseLocal){
  2785.                 pline->is_var   = 1;
  2786.                 pline->var = v;
  2787.                 pline++;
  2788.             }
  2789.             continue;
  2790.         }
  2791.  
  2792.         /*--value is non-empty, store it handling quotes and trailing space--*/
  2793.         if(*value == '"' && !v->is_list){
  2794.             was_quoted = 1;
  2795.             value++;
  2796.             for(p1 = value; *p1 && *p1 != '"'; p1++);
  2797.             if(*p1 == '"')
  2798.               *p1 = '\0';
  2799.             else
  2800.               removing_trailing_white_space(value);
  2801.         }else{
  2802.             removing_trailing_white_space(value);
  2803.             was_quoted = 0;
  2804.         }
  2805.  
  2806.     /*
  2807.      * List Entry Parsing
  2808.      *
  2809.      * The idea is to parse a comma separated list of 
  2810.      * elements, preserving quotes, and understanding
  2811.      * continuation lines (that is ',' == "\n ").
  2812.      * Quotes must be balanced within elements.  Space 
  2813.      * within elements is preserved, but leading and trailing 
  2814.      * space is trimmed.  This is a generic function, and it's 
  2815.      * left to the the functions that use the lists to make sure
  2816.      * they contain valid data...
  2817.      */
  2818.     if(v->is_list){
  2819.  
  2820.         was_quoted = 0;
  2821.         line_count = 0;
  2822.         p1         = value;
  2823.         while(1){            /* generous count of list elements */
  2824.         if(*p1 == '"')        /* ignore ',' if quoted   */
  2825.           was_quoted = (was_quoted) ? 0 : 1 ;
  2826.  
  2827.         if((*p1 == ',' && !was_quoted) || *p1 == '\n' || *p1 == '\0')
  2828.           line_count++;        /* count this element */
  2829.  
  2830.         if(*p1 == '\0' || *p1 == '\n'){    /* deal with EOL */
  2831.             if(p1 < p || *p1 == '\n'){
  2832.             *p1++ = ',';     /* fix null or newline */
  2833.  
  2834.             if(*p1 != '\t' && *p1 != ' '){
  2835.                 *(p1-1) = '\0'; /* tie off list */
  2836.                 p       = p1;   /* reset p */
  2837.                 break;
  2838.             }
  2839.             }else{
  2840.             p = p1;        /* end of pinerc */
  2841.             break;
  2842.             }
  2843.         }else
  2844.           p1++;
  2845.         }
  2846.  
  2847.         error  = NULL;
  2848.         lvalue = parse_list(value, line_count, &error);
  2849.         if(error){
  2850.         dprint(1, (debugfile,
  2851.                "read_pinerc: ERROR: %s in %s = \"%s\"\n", 
  2852.                error, v->name, value));
  2853.         }
  2854.         /*
  2855.          * Special case: turn "" strings into empty strings.
  2856.          * This allows users to turn off default lists.  For example,
  2857.          * if smtp-server is set then a user could override smtp-server
  2858.          * with smtp-server="".
  2859.          */
  2860.         for(i = 0; lvalue[i]; i++)
  2861.         if(lvalue[i][0] == '"' &&
  2862.            lvalue[i][1] == '"' &&
  2863.            lvalue[i][2] == '\0')
  2864.              lvalue[i][0] = '\0';
  2865.     }
  2866.  
  2867.         if(which_vars == ParseLocal){
  2868.             if(v->is_user){
  2869.         if(v->is_list){
  2870.             if(v->user_val.l){
  2871.             char **p;
  2872.             for(p = v->user_val.l; *p ; p++)
  2873.               fs_give((void **)p);
  2874.  
  2875.             fs_give((void **)&(v->user_val.l));
  2876.             }
  2877.  
  2878.             v->user_val.l = lvalue;
  2879.         }else{
  2880.             if(v->user_val.p != NULL)
  2881.               fs_give((void **) &(v->user_val.p));
  2882.  
  2883.             v->user_val.p = cpystr(value);
  2884.  
  2885.         }
  2886.  
  2887.         pline->is_var    = 1;
  2888.         pline->var  = v;
  2889.         pline->is_quoted = was_quoted;
  2890.         pline++;
  2891.             }
  2892.         }else if(which_vars == ParseGlobal){
  2893.             if(v->is_global){
  2894.         if(v->is_list){
  2895.             if(v->global_val.l){
  2896.             char **p;
  2897.             for(p = v->global_val.l; *p ; p++)
  2898.               fs_give((void **)p);
  2899.  
  2900.             fs_give((void **)&(v->global_val.l));
  2901.             }
  2902.  
  2903.             v->global_val.l = lvalue;
  2904.         }else{
  2905.             if(v->global_val.p != NULL)
  2906.               fs_give((void **) &(v->global_val.p));
  2907.             v->global_val.p = cpystr(value);
  2908.         }
  2909.             }
  2910.         }else{  /* which_vars == ParseFixed */
  2911.             if(v->is_user || v->is_global){
  2912.         if(v->is_list){
  2913.             if(v->fixed_val.l){
  2914.             char **p;
  2915.             for(p = v->fixed_val.l; *p ; p++)
  2916.               fs_give((void **)p);
  2917.  
  2918.             fs_give((void **)&(v->fixed_val.l));
  2919.             }
  2920.  
  2921.             v->fixed_val.l = lvalue;
  2922.         }else{
  2923.             if(v->fixed_val.p != NULL)
  2924.               fs_give((void **) &(v->fixed_val.p));
  2925.             v->fixed_val.p = cpystr(value);
  2926.         }
  2927.         }
  2928.     }
  2929.  
  2930. #ifdef DEBUG
  2931.     if(v->is_list){
  2932.         char **t;
  2933.         t =   which_vars == ParseLocal  ? v->user_val.l
  2934.             : which_vars == ParseGlobal ? v->global_val.l
  2935.         :                             v->fixed_val.l;
  2936.         if(t && *t && **t){
  2937.                 dprint(3,(debugfile, " %20.20s : %s\n", v->name, *t));
  2938.             while(++t && *t && **t)
  2939.                     dprint(3,(debugfile, " %20.20s : %s\n", "", *t));
  2940.         }
  2941.     }else{
  2942.         char *t;
  2943.         t =   which_vars == ParseLocal  ? v->user_val.p
  2944.             : which_vars == ParseGlobal ? v->global_val.p
  2945.         :                             v->fixed_val.p;
  2946.         if(t && *t)
  2947.                 dprint(3,(debugfile, " %20.20s : %s\n", v->name, t));
  2948.     }
  2949. #endif /* DEBUG */
  2950.     }
  2951.     if(which_vars == ParseLocal){
  2952.         pline->line = NULL;
  2953.         pline->is_var = 0;
  2954.     }
  2955.     fs_give((void **)&file);
  2956. }
  2957.  
  2958.  
  2959. /*
  2960.  * parse_list - takes a comma delimited list of "count" elements and 
  2961.  *              returns an array of pointers to each element neatly
  2962.  *              malloc'd in its own array.  Any errors are returned
  2963.  *              in the string pointed to by "error"
  2964.  *
  2965.  *  NOTE: only recognizes escaped quotes
  2966.  */
  2967. char **
  2968. parse_list(list, count, error)
  2969.     char *list, **error;
  2970.     int   count;
  2971. {
  2972.     char **lvalue, *p2, *p3, *p4;
  2973.     int    was_quoted = 0;
  2974.  
  2975.     lvalue = (char **)fs_get((count+1)*sizeof(char *));
  2976.     count  = 0;
  2977.     while(*list){            /* pick elements from list */
  2978.     p2 = list;        /* find end of element */
  2979.     while(1){
  2980.         if(*p2 == '"')    /* ignore ',' if quoted   */
  2981.           was_quoted = (was_quoted) ? 0 : 1 ;
  2982.  
  2983.         if(*p2 == '\\' && *(p2+1) == '"')
  2984.           p2++;        /* preserve escaped quotes, too */
  2985.  
  2986.         if((*p2 == ',' && !was_quoted) || *p2 == '\0')
  2987.           break;
  2988.  
  2989.         p2++;
  2990.     }
  2991.  
  2992.     if(was_quoted){        /* unbalanced parens! */
  2993.         if(error)
  2994.           *error = "Unbalanced parentheses";
  2995.  
  2996.         break;
  2997.     }
  2998.  
  2999.     /*
  3000.      * if element found, eliminate trailing 
  3001.      * white space and tie into variable list
  3002.      */
  3003.     if(p2 != list){
  3004.         for(p3 = p2 - 1; isspace((unsigned char)*p3) && list < p3; p3--)
  3005.           ;
  3006.  
  3007.         p4 = fs_get(((p3 - list) + 2) * sizeof(char));
  3008.         lvalue[count++] = p4;
  3009.         while(list <= p3)
  3010.           *p4++ = *list++;
  3011.  
  3012.         *p4 = '\0';
  3013.     }
  3014.  
  3015.     if(*(list = p2) != '\0'){    /* move to beginning of next val */
  3016.         while(*list == ',' || isspace((unsigned char)*list))
  3017.           list++;
  3018.     }
  3019.     }
  3020.  
  3021.     lvalue[count] = NULL;        /* tie off pointer list */
  3022.     return(lvalue);
  3023. }
  3024.  
  3025.  
  3026. static char quotes[3] = {'"', '"', '\0'};
  3027. /*----------------------------------------------------------------------
  3028.     Write out the .pinerc state information
  3029.  
  3030.    Args: ps -- The pine structure to take state to be written from
  3031.  
  3032.   This writes to a temporary file first, and then renames that to 
  3033.  be the new .pinerc file to protect against disk error.  This has the 
  3034.  problem of possibly messing up file protections, ownership and links.
  3035.   ----*/
  3036. write_pinerc(ps)
  3037.     struct pine *ps;
  3038. {
  3039.     char                buf[MAXPATH+1], copyright[90], *p, *dir, *tmp;
  3040.     int            write_trademark = 0;
  3041.     FILE               *f;
  3042.     struct pinerc_line *pline;
  3043.     struct variable    *var;
  3044.  
  3045.     dprint(2,(debugfile,"---- write_pinerc ----\n"));
  3046.  
  3047.     /* don't write if pinerc is read-only */
  3048.     if(ps->readonly_pinerc ||
  3049.          (can_access(ps->pinerc, ACCESS_EXISTS) == 0 &&
  3050.           can_access(ps->pinerc, EDIT_ACCESS) != 0)){
  3051.     ps->readonly_pinerc = 1;
  3052.     dprint(2,
  3053.         (debugfile,"write_pinerc: fail because can't access pinerc\n"));
  3054.     return(-1);
  3055.     }
  3056.  
  3057.     dir = ".";
  3058.     if(p = last_cmpnt(ps->pinerc)){
  3059.     *--p = '\0';
  3060.     dir = ps->pinerc;
  3061.     }
  3062.  
  3063. #if    defined(DOS) || defined(OS2)
  3064.     if(!(isalpha((unsigned char)dir[0]) && dir[1] == ':' && dir[2] == '\0')
  3065.        && (can_access(dir, EDIT_ACCESS) < 0 &&
  3066. #ifdef    DOS
  3067.        mkdir(dir) < 0)){
  3068. #else
  3069.        mkdir(dir,0700) < 0)) {
  3070. #endif
  3071.     q_status_message2(SM_ORDER | SM_DING, 3, 5,
  3072.               "Error creating \"%s\" : %s", dir,
  3073.               error_description(errno));
  3074.     return(-1);
  3075.     }
  3076.  
  3077.     tmp = temp_nam(dir, "rc");
  3078.     if(p)
  3079.       *p = '\\';
  3080.  
  3081.     if(tmp == NULL)
  3082.       goto io_err;
  3083.  
  3084.     strcpy(buf, tmp);
  3085.     free(tmp);
  3086.     f = fopen(buf, "wt");
  3087. #else  /* !DOS */
  3088.     tmp = temp_nam((*dir) ? dir : "/", "pinerc");
  3089.     if(p)
  3090.       *p = '/';
  3091.  
  3092.     if(tmp == NULL)
  3093.       goto io_err;
  3094.  
  3095.     strcpy(buf, tmp);
  3096.     free(tmp);
  3097.     f = fopen(buf, "w");
  3098. #endif  /* !DOS */
  3099.  
  3100.     if(f == NULL) 
  3101.       goto io_err;
  3102.  
  3103.     for(var = ps->vars; var->name != NULL; var++) 
  3104.       var->been_written = 0;
  3105.  
  3106.     /* copyright stuff starts here */
  3107.     pline = pinerc_lines;
  3108.     if(ps->first_time_user ||
  3109.        (ps->show_new_version && !trademark_lines_are_there))
  3110.       write_trademark = 1;
  3111.  
  3112.     /* if it was already there, remove old one */
  3113.     if(copyright_line_is_there)
  3114.       pline++;
  3115.  
  3116.     if(fprintf(f, "%s%s%s", cf_text_comment1, pine_version,
  3117.            cf_text_comment2) == EOF)
  3118.       goto io_err;
  3119.     if(write_trademark && fprintf(f, "%s%s", cf_text_comment3,
  3120.                   ps->first_time_user ? "" : "\n") == EOF)
  3121.       goto io_err;
  3122.     /* end of copyright stuff */
  3123.  
  3124.     /* Write out what was in the .pinerc */
  3125.     if(pline != NULL){
  3126.     for(; pline->is_var || pline->line != NULL; pline++){
  3127.         if(pline->is_var && !pline->obsolete_var){
  3128.         var = pline->var;
  3129.         if((var->is_list && (!var->user_val.l || !var->user_val.l[0]))
  3130.            || (!var->is_list && !var->user_val.p)){
  3131.             if(fprintf(f, "%s=\n", pline->var->name) == EOF)
  3132.               goto io_err;
  3133.         }
  3134.         else if((var->is_list && *var->user_val.l[0] == '\0')
  3135.              || (!var->is_list && *var->user_val.p == '\0')){
  3136.             if(fprintf(f, "%s=%s\n", pline->var->name, quotes) == EOF)
  3137.               goto io_err;
  3138.         }
  3139.         else{
  3140.             if(var->is_list){
  3141.             int i = 0;
  3142.  
  3143.             for(i = 0; var->user_val.l[i]; i++)
  3144.               if(fprintf(f, "%s%s%s%s\n",
  3145.                      (i) ? "\t" : var->name,
  3146.                      (i) ? "" : "=",
  3147.                      var->user_val.l[i][0] 
  3148.                        ? var->user_val.l[i] : quotes,
  3149.                      var->user_val.l[i+1] ? ",":"") == EOF)
  3150.                 goto io_err;
  3151.             }
  3152.             else{
  3153.             if(fprintf(f, "%s=%s%s%s\n", pline->var->name,
  3154.                    (pline->is_quoted
  3155.                     && *pline->var->user_val.p != '\"')
  3156.                      ? "\"" : "",
  3157.                    pline->var->user_val.p,
  3158.                    (pline->is_quoted
  3159.                     && *pline->var->user_val.p != '\"')
  3160.                      ? "\"" : "") == EOF)
  3161.               goto io_err;
  3162.             }
  3163.         }
  3164.  
  3165.         pline->var->been_written = 1;
  3166.         }else{
  3167.         /*
  3168.          * The description text should be changed into a message
  3169.          * about the variable being obsolete when a variable is
  3170.          * moved to obsolete status.  We add that message before
  3171.          * the variable unless it is already there.  However, we
  3172.          * leave the variable itself in case the user runs an old
  3173.          * version of pine again.  Note that we have read in the
  3174.          * value of the variable in read_pinerc and translated it
  3175.          * into a new variable if appropriate.
  3176.          */
  3177.         if(pline->obsolete_var){
  3178.             if(pline <= pinerc_lines || (pline-1)->line == NULL ||
  3179.                strlen((pline-1)->line) < 3 ||
  3180.                strucmp((pline-1)->line+2, pline->var->descrip) != 0)
  3181.               if(fprintf(f, "# %s\n", pline->var->descrip) == EOF)
  3182.             goto io_err;
  3183.         }
  3184.  
  3185.         if(fprintf(f, "%s\n", pline->line) == EOF)
  3186.           goto io_err;
  3187.         }
  3188.     }
  3189.     }
  3190.  
  3191.     /* Now write out all the variables not in the .pinerc */
  3192.     for(var = ps->vars; var->name != NULL; var++){
  3193.     if(!var->is_user || var->been_written || !var->is_used
  3194.        || var->is_obsolete)
  3195.       continue;
  3196.  
  3197.     dprint(5,(debugfile,"write_pinerc: %s = %s\n", var->name,
  3198.           var->user_val.p ? var->user_val.p : "<not set>"));
  3199.  
  3200.     /* Add extra comments about categories of variables */
  3201.     if(ps->first_time_user){
  3202.         if(var == &variables[V_PERSONAL_NAME]){
  3203.         if(fprintf(f, "\n%s\n", cf_before_personal_name) == EOF)
  3204.           goto io_err;
  3205.         }
  3206.         else if(var == &variables[V_INCOMING_FOLDERS]){
  3207.         if(fprintf(f, "\n%s\n", cf_before_incoming_folders) == EOF)
  3208.           goto io_err;
  3209.         }
  3210.         else if(var == &variables[V_FEATURE_LIST]){
  3211.         if(fprintf(f, "\n%s\n", cf_before_feature_list) == EOF)
  3212.           goto io_err;
  3213.         }
  3214.         else if(var == &variables[V_PRINTER]){
  3215.         if(fprintf(f, "\n%s\n", cf_before_printer) == EOF)
  3216.           goto io_err;
  3217.         }
  3218.     }
  3219.  
  3220.     /*
  3221.      * set description to NULL to eliminate preceding
  3222.      * blank and comment line.
  3223.      */
  3224.     if(var->descrip && *var->descrip
  3225.        && fprintf(f, "\n# %s\n", var->descrip) == EOF)
  3226.       goto io_err;
  3227.  
  3228.     if((var->is_list && (!var->user_val.l
  3229.                  || (!var->user_val.l[0] && !var->global_val.l)))
  3230.        || (!var->is_list && !var->user_val.p)){
  3231.         if(fprintf(f, "%s=\n", var->name) == EOF)
  3232.           goto io_err;
  3233.     }
  3234.     else if((var->is_list
  3235.          && (!var->user_val.l[0] || !var->user_val.l[0][0]))
  3236.         || (!var->is_list && var->user_val.p[0] == '\0')){
  3237.         if(fprintf(f, "%s=\"\"\n", var->name) == EOF)
  3238.           goto io_err;
  3239.     }
  3240.     else if(var->is_list){
  3241.         int i = 0;
  3242.  
  3243.         for(i = 0; var->user_val.l[i] ; i++)
  3244.           if(fprintf(f, "%s%s%s%s\n", (i) ? "\t" : var->name,
  3245.              (i) ? "" : "=", var->user_val.l[i],
  3246.              var->user_val.l[i+1] ? ",":"") == EOF)
  3247.         goto io_err;
  3248.     }
  3249.     else{
  3250.         if(fprintf(f, "%s=%s\n", var->name, var->user_val.p) == EOF)
  3251.           goto io_err;
  3252.     }
  3253.     }
  3254.  
  3255.     if(fclose(f) == EOF || rename_file(buf, ps->pinerc) < 0)
  3256.       goto io_err;
  3257.  
  3258.     ps->outstanding_pinerc_changes = 0;
  3259.  
  3260.     return(0);
  3261.  
  3262.   io_err:
  3263.     q_status_message2(SM_ORDER | SM_DING, 3, 5,
  3264.               "Error saving configuration in file \"%s\": %s",
  3265.               ps->pinerc, error_description(errno));
  3266.     dprint(1, (debugfile, "Error writing %s : %s\n", ps->pinerc,
  3267.            error_description(errno)));
  3268.     return(-1);
  3269. }
  3270.  
  3271.  
  3272. /*------------------------------------------------------------
  3273.   Return TRUE if the given string was a feature name present in the
  3274.   pinerc as it was when pine was started...
  3275.   ----*/
  3276. var_in_pinerc(s)
  3277. char *s;
  3278. {
  3279.     struct pinerc_line *pline;
  3280.     for(pline = pinerc_lines; pline->var || pline->line; pline++)
  3281.       if(pline->var && pline->var->name && !strucmp(s, pline->var->name))
  3282.     return(1);
  3283.  
  3284.     return(0);
  3285. }
  3286.  
  3287.  
  3288.  
  3289. /*------------------------------------------------------------
  3290.   Free resources associated with static pinerc_lines data
  3291.  ----*/
  3292. void
  3293. free_pinerc_lines()
  3294. {
  3295.     struct pinerc_line *pline;
  3296.  
  3297.     if(pline = pinerc_lines){
  3298.     for( ; pline->var || pline->line; pline++)
  3299.       if(pline->line)
  3300.         fs_give((void **)&pline->line);
  3301.  
  3302.     fs_give((void **)&pinerc_lines);
  3303.     }
  3304. }
  3305.  
  3306.  
  3307.  
  3308. /*
  3309.  * This is only used at startup time.  It sets ps->def_sort and
  3310.  * ps->def_sort_rev.  The syntax of the sort_spec is type[/reverse].
  3311.  * A reverse without a type is the same as arrival/reverse.  A blank
  3312.  * argument also means arrival/reverse.
  3313.  */
  3314. int
  3315. decode_sort(ps, sort_spec)
  3316.      struct pine *ps;
  3317.      char        *sort_spec;
  3318. {
  3319.     char *sep;
  3320.     int    x, reverse;
  3321.  
  3322.     if(*sort_spec == '\0' ||
  3323.            struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){
  3324.     ps->def_sort_rev = 1;
  3325.         return(0);
  3326.     }
  3327.      
  3328.     reverse = 0;
  3329.     if((sep = strindex(sort_spec, '/')) != NULL){
  3330.         *sep = '\0';
  3331.         sep++;
  3332.         if(struncmp(sep, "reverse", strlen(sep)) == 0)
  3333.           reverse = 1;
  3334.         else
  3335.           return(-1);
  3336.     }
  3337.  
  3338.     for(x = 0; ps_global->sort_types[x] != EndofList; x++)
  3339.       if(struncmp(sort_name(ps_global->sort_types[x]),
  3340.                   sort_spec, strlen(sort_spec)) == 0)
  3341.         break;
  3342.  
  3343.     if(ps_global->sort_types[x] == EndofList)
  3344.       return(-1);
  3345.  
  3346.     ps->def_sort     = ps_global->sort_types[x];
  3347.     ps->def_sort_rev = reverse;
  3348.     return(0);
  3349. }
  3350.  
  3351.  
  3352. /*------------------------------------------------------------
  3353.     Dump out a global pine.conf on the standard output with fresh
  3354.     comments.  Preserves variables currently set in SYSTEM_PINERC, if any.
  3355.   ----*/
  3356. void
  3357. dump_global_conf()
  3358. {
  3359.      FILE            *f;
  3360.      struct variable *var;
  3361.  
  3362.      read_pinerc(SYSTEM_PINERC, variables, ParseGlobal);
  3363.  
  3364.      f = stdout;
  3365.      if(f == NULL) 
  3366.        goto io_err;
  3367.  
  3368.      fprintf(f, "#      %s -- system wide pine configuration\n#\n",
  3369.          SYSTEM_PINERC);
  3370.      fprintf(f, "# Values here affect all pine users unless they've overidden the values\n");
  3371.      fprintf(f, "# in their .pinerc files.  A copy of this file with current comments may\n");
  3372.      fprintf(f, "# be obtained by running \"pine -conf\". It will be printed to standard output.\n#\n");
  3373.      fprintf(f,"# For a variable to be unset its value must be null/blank.  This is not the\n");
  3374.      fprintf(f,"# same as the value of \"empty string\", which can be used to effectively\n");
  3375.      fprintf(f,"# \"unset\" a variable that has a default or previously assigned value.\n");
  3376.      fprintf(f,"# To set a variable to the empty string its value should be \"\".\n");
  3377.      fprintf(f,"# Switch variables are set to either \"yes\" or \"no\", and default to \"no\".\n");
  3378.      fprintf(f,"# Except for feature-list items, which are additive, values set in the\n");
  3379.      fprintf(f,"# .pinerc file replace those in pine.conf, and those in pine.conf.fixed\n");
  3380.      fprintf(f,"# over-ride all others.  Features can be over-ridden in .pinerc or\n");
  3381.      fprintf(f,"# pine.conf.fixed by pre-pending the feature name with \"no-\".\n#\n");
  3382.      fprintf(f,"# (These comments are automatically inserted.)\n");
  3383.  
  3384.      for(var = variables; var->name != NULL; var++){
  3385.          dprint(5,(debugfile,"write_pinerc: %s = %s\n", var->name,
  3386.                    var->user_val.p ? var->user_val.p : "<not set>"));
  3387.          if(!var->is_global || !var->is_used || var->is_obsolete)
  3388.            continue;
  3389.  
  3390.          if(var->descrip && *var->descrip){
  3391.            if(fprintf(f, "\n# %s\n", var->descrip) == EOF)
  3392.              goto io_err;
  3393.      }
  3394.  
  3395.      if(var->is_list){
  3396.          if(var->global_val.l == NULL){
  3397.          if(fprintf(f, "%s=\n", var->name) == EOF)
  3398.            goto io_err;
  3399.          }else{
  3400.          int i;
  3401.  
  3402.          for(i=0; var->global_val.l[i]; i++)
  3403.            if(fprintf(f, "%s%s%s%s\n", (i) ? "\t" : var->name,
  3404.                   (i) ? "" : "=", var->global_val.l[i],
  3405.                   var->global_val.l[i+1] ? ",":"") == EOF)
  3406.              goto io_err;
  3407.          }
  3408.      }else{
  3409.          if(var->global_val.p == NULL){
  3410.          if(fprintf(f, "%s=\n", var->name) == EOF)
  3411.            goto io_err;
  3412.          }else if(strlen(var->global_val.p) == 0){
  3413.          if(fprintf(f, "%s=\"\"\n", var->name) == EOF)
  3414.                goto io_err;
  3415.          }else{
  3416.          if(fprintf(f,"%s=%s\n",var->name,var->global_val.p) == EOF)
  3417.            goto io_err;
  3418.          }
  3419.      }
  3420.      }
  3421.      exit(0);
  3422.  
  3423.  
  3424.    io_err:
  3425.      fprintf(stderr, "Error writing config to stdout: %s\n",
  3426.              error_description(errno));
  3427.      exit(-1);
  3428. }
  3429.  
  3430.  
  3431. /*------------------------------------------------------------
  3432.     Dump out a pinerc to filename with fresh
  3433.     comments.  Preserves variables currently set in pinerc, if any.
  3434.   ----*/
  3435. void
  3436. dump_new_pinerc(filename)
  3437. char *filename;
  3438. {
  3439.     FILE            *f;
  3440.     struct variable *var;
  3441.     char             buf[MAXPATH];
  3442.  
  3443. #if defined(DOS) || defined(OS2)
  3444.     if(!ps_global->pinerc){
  3445.     char *p;
  3446.     int   l;
  3447.  
  3448.     if(p = getenv("PINERC")){
  3449.         ps_global->pinerc = cpystr(p);
  3450.     }else{
  3451.         char buf2[MAXPATH];
  3452.         build_path(buf2, ps_global->home_dir, DF_PINEDIR);
  3453.         build_path(buf, buf2, SYSTEM_PINERC);
  3454.     }
  3455.     }
  3456. #else    /* !DOS */
  3457.     if(!ps_global->pinerc)
  3458.     build_path(buf, ps_global->home_dir, ".pinerc");
  3459. #endif    /* !DOS */
  3460.  
  3461.     read_pinerc(buf, variables, ParseLocal);
  3462.  
  3463.     f = NULL;;
  3464.     if(filename[0] == '\0'){
  3465.     fprintf(stderr, "Missing argument to \"-pinerc\".\n");
  3466.     }else if(!strcmp(filename, "-")){
  3467.     f = stdout;
  3468.     }else{
  3469.     f = fopen(filename, "w");
  3470.     }
  3471.  
  3472.     if(f == NULL) 
  3473.     goto io_err;
  3474.  
  3475.     if(fprintf(f, "%s%s%s", cf_text_comment1, pine_version,
  3476.            cf_text_comment2) == EOF)
  3477.     goto io_err;
  3478.     if(fprintf(f, "%s", cf_text_comment3) == EOF)
  3479.     goto io_err;
  3480.  
  3481.     for(var = variables; var->name != NULL; var++){
  3482.     dprint(5,(debugfile,"write_pinerc: %s = %s\n", var->name,
  3483.                    var->user_val.p ? var->user_val.p : "<not set>"));
  3484.         if(!var->is_user || !var->is_used || var->is_obsolete)
  3485.         continue;
  3486.  
  3487.     /* Add extra comments about categories of variables */
  3488.         if(var == &variables[V_PERSONAL_NAME]){
  3489.         if(fprintf(f, "\n%s\n", cf_before_personal_name) == EOF)
  3490.         goto io_err;
  3491.         }else if(var == &variables[V_INCOMING_FOLDERS]){
  3492.         if(fprintf(f, "\n%s\n", cf_before_incoming_folders) == EOF)
  3493.         goto io_err;
  3494.         }else if(var == &variables[V_FEATURE_LIST]){
  3495.         if(fprintf(f, "\n%s\n", cf_before_feature_list) == EOF)
  3496.         goto io_err;
  3497.         }else if(var == &variables[V_PRINTER]){
  3498.         if(fprintf(f, "\n%s\n", cf_before_printer) == EOF)
  3499.         goto io_err;
  3500.         }
  3501.  
  3502.     /*
  3503.      * set description to NULL to eliminate preceding
  3504.      * blank and comment line.
  3505.      */
  3506.          if(var->descrip && *var->descrip){
  3507.            if(fprintf(f, "\n# %s\n", var->descrip) == EOF)
  3508.              goto io_err;
  3509.      }
  3510.  
  3511.     if(var->is_list){
  3512.         if(var->user_val.l == NULL){
  3513.         if(fprintf(f, "%s=\n", var->name) == EOF)
  3514.             goto io_err;
  3515.         }else{
  3516.         int i;
  3517.  
  3518.         for(i=0; var->user_val.l[i]; i++)
  3519.             if(fprintf(f, "%s%s%s%s\n", (i) ? "\t" : var->name,
  3520.                   (i) ? "" : "=", var->user_val.l[i],
  3521.                   var->user_val.l[i+1] ? ",":"") == EOF)
  3522.             goto io_err;
  3523.         }
  3524.     }else{
  3525.         if(var->user_val.p == NULL){
  3526.         if(fprintf(f, "%s=\n", var->name) == EOF)
  3527.             goto io_err;
  3528.         }else if(strlen(var->user_val.p) == 0){
  3529.         if(fprintf(f, "%s=\"\"\n", var->name) == EOF)
  3530.             goto io_err;
  3531.         }else{
  3532.         if(fprintf(f,"%s=%s\n",var->name,var->user_val.p) == EOF)
  3533.             goto io_err;
  3534.         }
  3535.     }
  3536.     }
  3537.     exit(0);
  3538.  
  3539.  
  3540. io_err:
  3541.     fprintf(stderr, "Error writing config to %s: %s\n",
  3542.              filename, error_description(errno));
  3543.     exit(-1);
  3544. }
  3545.  
  3546.  
  3547. /*----------------------------------------------------------------------
  3548.   Dump the whole config enchilada using the given function
  3549.    
  3550.   Args: ps -- pine struct containing vars to dump
  3551.     pc -- function to use to write the config data
  3552.  
  3553.  Result: command line, global, user var's written with given function
  3554.  
  3555.  ----*/ 
  3556. void
  3557. dump_config(ps, pc)
  3558.     struct pine *ps;
  3559.     gf_io_t pc;
  3560. {
  3561.     int           i;
  3562.     char       quotes[3], tmp[MAILTMPLEN];
  3563.     register struct variable *vars;
  3564.     NAMEVAL_S *feat;
  3565.  
  3566.     quotes[0] = '"'; quotes[1] = '"'; quotes[2] = '\0';
  3567.  
  3568.     for(i=0; i < 5; i++){
  3569.     sprintf(tmp, "======= %s_val options set",
  3570.         (i == 0) ? "Current" :
  3571.          (i == 1) ? "Command_line" :
  3572.           (i == 2) ? "User" :
  3573.            (i == 3) ? "Global"
  3574.                 : "Fixed");
  3575.     gf_puts(tmp, pc);
  3576.     if(i > 1){
  3577.         sprintf(tmp, " (%s)",
  3578.             (i == 2) ? ((ps->pinerc) ? ps->pinerc
  3579.                          : ".pinerc") :
  3580.             (i == 3) ? ((ps->pine_conf) ? ps->pine_conf
  3581.                         : SYSTEM_PINERC) :
  3582. #if defined(DOS) || defined(OS2)
  3583.             "NO FIXED"
  3584. #else
  3585.             ((can_access(SYSTEM_PINERC_FIXED, ACCESS_EXISTS) == 0)
  3586.                  ? SYSTEM_PINERC_FIXED : "NO pine.conf.fixed")
  3587. #endif
  3588.             );
  3589.         gf_puts(tmp, pc);
  3590.     }
  3591.  
  3592.     gf_puts(" =======\n", pc);
  3593.     for(vars = ps->vars; vars->name; vars++){
  3594.         if(vars->is_list){
  3595.         char **t;
  3596.         t = (i == 0) ? vars->current_val.l :
  3597.              (i == 1) ? vars->cmdline_val.l :
  3598.               (i == 2) ? vars->user_val.l :
  3599.             (i == 3) ? vars->global_val.l
  3600.                  : vars->fixed_val.l;
  3601.         if(t && *t){
  3602.             sprintf(tmp, " %20.20s : %s\n", vars->name,
  3603.                 **t ? *t : quotes);
  3604.             gf_puts(tmp, pc);
  3605.             while(++t && *t){
  3606.             sprintf(tmp," %20.20s : %s\n","",**t ? *t : quotes);
  3607.             gf_puts(tmp, pc);
  3608.             }
  3609.         }
  3610.         }
  3611.         else{
  3612.         char *t;
  3613.         t = (i == 0) ? vars->current_val.p :
  3614.              (i == 1) ? vars->cmdline_val.p :
  3615.               (i == 2) ? vars->user_val.p :
  3616.                (i == 3) ? vars->global_val.p
  3617.                 : vars->fixed_val.p;
  3618.         if(t){
  3619.             sprintf(tmp, " %20.20s : %s\n", vars->name,
  3620.                 *t ? t : quotes);
  3621.             gf_puts(tmp, pc);
  3622.         }
  3623.         }
  3624.     }
  3625.     }
  3626.  
  3627.     gf_puts("========== Feature settings ==========\n", pc);
  3628.     for(i = 0; feat = feature_list(i) ; i++)
  3629.       if(feat->value != F_OLD_GROWTH){
  3630.       sprintf(tmp, "  %s%s\n", F_ON(feat->value,ps) ? "   " : "no-",
  3631.           feat->name);
  3632.       gf_puts(tmp, pc);
  3633.       }
  3634. }
  3635.  
  3636.  
  3637. /*----------------------------------------------------------------------
  3638.   Dump interesting variables from the given pine struct
  3639.    
  3640.   Args: ps -- pine struct to dump 
  3641.     pc -- function to use to write the config data
  3642.  
  3643.  Result: various important pine struct data written
  3644.  
  3645.  ----*/ 
  3646. void
  3647. dump_pine_struct(ps, pc)
  3648.     struct pine *ps;
  3649.     gf_io_t pc;
  3650. {
  3651.     char *p;
  3652.     extern char term_name[];
  3653.  
  3654.     gf_puts("========== struct pine * ==========\n", pc);
  3655.     if(!ps){
  3656.     gf_puts("!No struct!\n", pc);
  3657.     return;
  3658.     }
  3659.  
  3660.     gf_puts("ui:\tlogin = ", pc);
  3661.     gf_puts((ps->ui.login) ? ps->ui.login : "NULL", pc);
  3662.     gf_puts(", full = ", pc);
  3663.     gf_puts((ps->ui.fullname) ? ps->ui.fullname : "NULL", pc);
  3664.     gf_puts("\n\thome = ", pc);
  3665.     gf_puts((ps->ui.homedir) ? ps->ui.homedir : "NULL", pc);
  3666.  
  3667.     gf_puts("\nhome_dir=\t", pc);
  3668.     gf_puts(ps->home_dir ? ps->home_dir : "NULL", pc);
  3669.     gf_puts("\nhostname=\t", pc);
  3670.     gf_puts(ps->hostname ? ps->hostname : "NULL", pc);
  3671.     gf_puts("\nlocaldom=\t", pc);
  3672.     gf_puts(ps->localdomain ? ps->localdomain : "NULL", pc);
  3673.     gf_puts("\nuserdom=\t", pc);
  3674.     gf_puts(ps->userdomain ? ps->userdomain : "NULL", pc);
  3675.     gf_puts("\nmaildom=\t", pc);
  3676.     gf_puts(ps->maildomain ? ps->maildomain : "NULL", pc);
  3677.  
  3678.     if(ps->mail_stream){
  3679.     gf_puts("\ncur_cntxt=\t", pc);
  3680.     gf_puts((ps->context_current && ps->context_current->context)
  3681.         ? ps->context_current->context : "None", pc);
  3682.     gf_puts("\ncur_fldr=\t", pc);
  3683.     gf_puts(ps->cur_folder, pc);
  3684.     gf_puts("\nactual mbox=\t", pc);
  3685.     gf_puts(ps->mail_stream->mailbox ? ps->mail_stream->mailbox
  3686.                      : "no mailbox!", pc);
  3687.     if(ps->msgmap){
  3688.         gf_puts("\nmsgmap: tot=", pc);
  3689.         gf_puts(long2string(mn_get_total(ps->msgmap)), pc);
  3690.         gf_puts(", cur=", pc);
  3691.         gf_puts(long2string(mn_get_cur(ps->msgmap)), pc);
  3692.         gf_puts(", del=", pc);
  3693.         gf_puts(long2string(count_flagged(ps->mail_stream, "DELETED")),pc);
  3694.         gf_puts(", hid=", pc);
  3695.         gf_puts(long2string(any_lflagged(ps->msgmap, MN_HIDE)), pc);
  3696.         gf_puts(", exld=", pc);
  3697.         gf_puts(long2string(any_lflagged(ps->msgmap, MN_EXLD)), pc);
  3698.         gf_puts(", slct=", pc);
  3699.         gf_puts(long2string(any_lflagged(ps->msgmap, MN_SLCT)), pc);
  3700.         gf_puts(", sort=", pc);
  3701.         if(mn_get_revsort(ps->msgmap))
  3702.           gf_puts("rev-", pc);
  3703.  
  3704.         gf_puts(sort_name(mn_get_sort(ps->msgmap)), pc);
  3705.     }
  3706.     else
  3707.       gf_puts("\nNo msgmap", pc);
  3708.     }
  3709.     else
  3710.       gf_puts("\nNo mail_stream", pc);
  3711.  
  3712.     if(ps->inbox_stream && (ps->mail_stream != ps->inbox_stream)){
  3713.     gf_puts("\nactual inbox=\t", pc);
  3714.     gf_puts(ps->inbox_stream->mailbox ? ps->inbox_stream->mailbox
  3715.                       : "no mailbox!", pc);
  3716.     if(ps->inbox_msgmap){
  3717.         gf_puts("\ninbox map: tot=", pc);
  3718.         gf_puts(long2string(mn_get_total(ps->inbox_msgmap)), pc);
  3719.         gf_puts(", cur=", pc);
  3720.         gf_puts(long2string(mn_get_cur(ps->inbox_msgmap)), pc);
  3721.         gf_puts(", del=", pc);
  3722.         gf_puts(long2string(count_flagged(ps->inbox_stream,"DELETED")),pc);
  3723.         gf_puts(", hid=", pc);
  3724.         gf_puts(long2string(any_lflagged(ps->inbox_msgmap, MN_HIDE)), pc);
  3725.         gf_puts(", exld=", pc);
  3726.         gf_puts(long2string(any_lflagged(ps->inbox_msgmap, MN_EXLD)), pc);
  3727.         gf_puts(", slct=", pc);
  3728.         gf_puts(long2string(any_lflagged(ps->inbox_msgmap, MN_SLCT)), pc);
  3729.         gf_puts(", sort=", pc);
  3730.         if(mn_get_revsort(ps->inbox_msgmap))
  3731.           gf_puts("rev-", pc);
  3732.  
  3733.         gf_puts(sort_name(mn_get_sort(ps->inbox_msgmap)), pc);
  3734.     }
  3735.     else
  3736.       gf_puts("\nNo inbox_map", pc);
  3737.     }
  3738.     else
  3739.       gf_puts(ps->inbox_stream ? "\ninbox is mail_stream"
  3740.                    : "\nno inbox stream", pc);
  3741.     gf_puts("\nterm ", pc);
  3742. #if !defined(DOS) && !defined(OS2)
  3743.     gf_puts("type=", pc);
  3744.     gf_puts(term_name, pc);
  3745.     gf_puts(", ttyname=", pc);
  3746.     gf_puts((p = (char *)ttyname(0)) ? p : "NONE", pc);
  3747. #endif
  3748.     gf_puts(", size=", pc);
  3749.     gf_puts(int2string(ps->ttyo->screen_rows), pc);
  3750.     gf_puts("x", pc);
  3751.     gf_puts(int2string(ps->ttyo->screen_cols), pc);
  3752.     gf_puts(", speed=", pc);
  3753.     gf_puts((ps->low_speed) ? "slow" : "normal", pc);
  3754.     gf_puts("\n", pc);
  3755. }
  3756.  
  3757.  
  3758. /*----------------------------------------------------------------------
  3759.       Set a user variable and save the .pinerc
  3760.    
  3761.   Args:  var -- The index of the variable to set from pine.h (V_....)
  3762.          value -- The string to set the value to
  3763.  
  3764.  Result: -1 is returned on failure and 0 is returned on success
  3765.  
  3766.  The vars data structure is updated and the pinerc saved.
  3767.  ----*/ 
  3768. set_variable(var, value, commit)
  3769.      int   var, commit;
  3770.      char *value;
  3771. {
  3772.     struct variable *v;
  3773.  
  3774.     v = &ps_global->vars[var];
  3775.  
  3776.     if(!v->is_user) 
  3777.       panic1("Trying to set non-user variable %s", v->name);
  3778.  
  3779.     if(v->user_val.p)
  3780.       fs_give((void **) &v->user_val.p);
  3781.  
  3782.     if(v->current_val.p)
  3783.       fs_give((void **) &v->current_val.p);
  3784.  
  3785.     v->user_val.p    = value ? cpystr(value) : NULL;
  3786.     v->current_val.p = value ? cpystr(value) : NULL;
  3787.  
  3788.     ps_global->outstanding_pinerc_changes = 1;
  3789.  
  3790.     return(commit ? write_pinerc(ps_global) : 1);
  3791. }
  3792.  
  3793.  
  3794. /*----------------------------------------------------------------------
  3795.       Set a user variable list and save the .pinerc
  3796.    
  3797.   Args:  var -- The index of the variable to set from pine.h (V_....)
  3798.          lvalue -- The list to set the value to
  3799.  
  3800.  Result: -1 is returned on failure and 0 is returned on success
  3801.  
  3802.  The vars data structure is updated and the pinerc saved.
  3803.  ----*/ 
  3804. set_variable_list(var, lvalue)
  3805.     int    var;
  3806.     char **lvalue;
  3807. {
  3808.     int              i;
  3809.     struct variable *v = &ps_global->vars[var];
  3810.  
  3811.     if(!v->is_user || !v->is_list)
  3812.       panic1("BOTCH: Trying to set non-user or non-list variable %s", v->name);
  3813.  
  3814.     if(v->user_val.l){
  3815.     for(i = 0; v->user_val.l[i] ; i++)
  3816.       fs_give((void **) &v->user_val.l[i]);
  3817.  
  3818.     fs_give((void **) &v->user_val.l);
  3819.     }
  3820.  
  3821.     if(v->current_val.l){
  3822.     for(i = 0; v->current_val.l[i] ; i++)
  3823.       fs_give((void **) &v->current_val.l[i]);
  3824.  
  3825.     fs_give((void **) &v->current_val.l);
  3826.     }
  3827.  
  3828. /* BUG: HAVING MULTIPLE COPIES OF CONFIG DATA IS BOGUS */
  3829.     if(lvalue){
  3830.     for(i = 0; lvalue[i] ; i++)    /* count elements */
  3831.       ;
  3832.  
  3833.     v->user_val.l    = (char **) fs_get((i+1) * sizeof(char *));
  3834.     v->current_val.l = (char **) fs_get((i+1) * sizeof(char *));
  3835.  
  3836.     for(i = 0; lvalue[i] ; i++){
  3837.         v->user_val.l[i]    = cpystr(lvalue[i]);
  3838.         v->current_val.l[i] = cpystr(lvalue[i]);
  3839.     }
  3840.  
  3841.     v->user_val.l[i]    = NULL;
  3842.     v->current_val.l[i] = NULL;
  3843.     }
  3844.  
  3845.  
  3846.     return(write_pinerc(ps_global));
  3847. }
  3848.            
  3849.  
  3850. /*----------------------------------------------------------------------
  3851.     Make sure the pine folders directory exists, with proper folders
  3852.  
  3853.    Args: ps -- pine structure to get mail directory and contexts from
  3854.  
  3855.   Result: returns 0 if it exists or it is created and all is well
  3856.                   1 if it is missing and can't be created.
  3857.   ----*/
  3858. init_mail_dir(ps)
  3859.     struct pine *ps;
  3860. {
  3861.     /*
  3862.      * We don't really care if mail_dir exists if it isn't 
  3863.      * part of the first folder collection specified.  If this
  3864.      * is the case, it must have been created external to us, so
  3865.      * just move one...
  3866.      */
  3867.     if(ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[0]){
  3868.     char *p  = context_string(ps->VAR_FOLDER_SPEC[0]);
  3869.     int   rv = strncmp(p, ps->VAR_MAIL_DIRECTORY,
  3870.                strlen(ps->VAR_MAIL_DIRECTORY));
  3871.     fs_give((void **)&p);
  3872.     if(rv)
  3873.       return(0);
  3874.     }
  3875.  
  3876.     switch(is_writable_dir(ps->folders_dir)){
  3877.       case 0:
  3878.         /* --- all is well --- */
  3879.     return(0);
  3880.  
  3881.       case 1:
  3882.     sprintf(tmp_20k_buf, init_md_exists, ps->folders_dir);
  3883.     display_init_err(tmp_20k_buf, 1);
  3884.     return(-1);
  3885.  
  3886.       case 2:
  3887.     sprintf(tmp_20k_buf, init_md_file, ps->folders_dir);
  3888.     display_init_err(tmp_20k_buf, 1);
  3889.     return(-1);
  3890.  
  3891.       case 3:
  3892.     sprintf(tmp_20k_buf, init_md_create, ps->folders_dir);
  3893.     display_init_err(tmp_20k_buf, 0);
  3894. #ifndef    _WINDOWS
  3895.         sleep(4);
  3896. #endif
  3897.         if(create_mail_dir(ps->folders_dir) < 0){
  3898.             sprintf(tmp_20k_buf, "Error creating subdirectory \"%s\" : %s",
  3899.             ps->folders_dir, error_description(errno));
  3900.         display_init_err(tmp_20k_buf, 1);
  3901.             return(-1);
  3902.         }
  3903.     }
  3904.  
  3905.     return(0);
  3906. }
  3907.  
  3908.  
  3909. /*----------------------------------------------------------------------
  3910.   Make sure the default save folders exist in the default
  3911.   save context.
  3912.   ----*/
  3913. void
  3914. display_init_err(s, err)
  3915.     char *s;
  3916.     int   err;
  3917. {
  3918. #ifdef    _WINDOWS
  3919.     mswin_messagebox(s, err);
  3920. #else
  3921.     int n = 0;
  3922.  
  3923.     if(err)
  3924.       fputc(BELL, stdout);
  3925.  
  3926.     for(; *s; s++)
  3927.       if(++n > 60 && isspace((unsigned char)*s)){
  3928.       n = 0;
  3929.       fputc('\n', stdout);
  3930.       while(*(s+1) && isspace((unsigned char)*(s+1)))
  3931.         s++;
  3932.       }
  3933.       else
  3934.     fputc(*s, stdout);
  3935.  
  3936.     fputc('\n', stdout);
  3937. #endif
  3938. }
  3939.  
  3940.  
  3941. /*----------------------------------------------------------------------
  3942.   Make sure the default save folders exist in the default
  3943.   save context.
  3944.   ----*/
  3945. void
  3946. init_save_defaults()
  3947. {
  3948.     CONTEXT_S  *save_cntxt;
  3949.  
  3950.     if(!ps_global->VAR_DEFAULT_FCC || !*ps_global->VAR_DEFAULT_FCC)
  3951.       return;
  3952.  
  3953.     if(!(save_cntxt = default_save_context(ps_global->context_list)))
  3954.       save_cntxt = ps_global->context_list;
  3955.  
  3956.     if(context_isambig(ps_global->VAR_DEFAULT_FCC)){
  3957.     find_folders_in_context(NULL, save_cntxt, ps_global->VAR_DEFAULT_FCC);
  3958.     if(folder_index(ps_global->VAR_DEFAULT_FCC, save_cntxt->folders) < 0)
  3959.       context_create(save_cntxt->context, NULL,
  3960.              ps_global->VAR_DEFAULT_FCC);
  3961.     }
  3962.  
  3963.     find_folders_in_context(NULL, save_cntxt,
  3964.                 ps_global->VAR_DEFAULT_SAVE_FOLDER);
  3965.     if(folder_index(ps_global->VAR_DEFAULT_SAVE_FOLDER,
  3966.             save_cntxt->folders) < 0)
  3967.       context_create(save_cntxt->context, NULL,
  3968.              ps_global->VAR_DEFAULT_SAVE_FOLDER);
  3969.  
  3970.     free_folders_in_context(save_cntxt);
  3971. }
  3972.  
  3973.  
  3974.  
  3975. /*----------------------------------------------------------------------
  3976.    Routines for pruning old Fcc, usually "sent-mail" folders.     
  3977.   ----*/
  3978. struct sm_folder {
  3979.     char *name;
  3980.     int   month_num;
  3981. };
  3982.  
  3983.  
  3984. /*
  3985.  * Pruning prototypes
  3986.  */
  3987. void     delete_old_mail PROTO((struct sm_folder *, CONTEXT_S *, char *));
  3988. struct     sm_folder *get_mail_list PROTO((CONTEXT_S *, char *));
  3989. int     prune_folders PROTO((CONTEXT_S *, char *, int, char *));
  3990.  
  3991.  
  3992.  
  3993. /*----------------------------------------------------------------------
  3994.       Put sent-mail files in date order 
  3995.  
  3996.    Args: a, b  -- The names of two files.  Expects names to be sent-mail-mmm-yy
  3997.                   Other names will sort in order and come before those
  3998.                   in above format.
  3999.  ----*/
  4000. int   
  4001. compare_sm_files(aa, bb)
  4002.     const QSType *aa, *bb;
  4003. {
  4004.     struct sm_folder *a = (struct sm_folder *)aa,
  4005.                      *b = (struct sm_folder *)bb;
  4006.  
  4007.     if(a->month_num == -1 && b->month_num == -1)
  4008.       return(strucmp(a->name, b->name));
  4009.     if(a->month_num == -1)      return(-1);
  4010.     if(b->month_num == -1)      return(1);
  4011.  
  4012.     return(a->month_num - b->month_num);
  4013. }
  4014.  
  4015.  
  4016.  
  4017. /*----------------------------------------------------------------------
  4018.       Create an ordered list of sent-mail folders and their month numbers
  4019.  
  4020.    Args: dir -- The directory to find the list of files in
  4021.  
  4022.  Result: Pointer to list of files is returned. 
  4023.  
  4024. This list includes all files that start with "sent-mail", but not "sent-mail" 
  4025. itself.
  4026.   ----*/
  4027. struct sm_folder *
  4028. get_mail_list(list_cntxt, folder_base)
  4029.     CONTEXT_S *list_cntxt;
  4030.     char      *folder_base;
  4031. {
  4032. #define MAX_FILES  (150)
  4033.     register struct sm_folder *sm  = NULL;
  4034.     struct sm_folder          *sml = NULL;
  4035.     char                      *filename;
  4036.     int                        i, folder_base_len;
  4037.     char               searchname[MAXPATH+1];
  4038.  
  4039.     sml = sm = (struct sm_folder *)fs_get(sizeof(struct sm_folder)*MAX_FILES);
  4040.     memset((void *)sml, 0, sizeof(struct sm_folder) * MAX_FILES);
  4041.     if((folder_base_len = strlen(folder_base)) == 0 || !list_cntxt){
  4042.         sml->name = cpystr("");
  4043.         return(sml);
  4044.     }
  4045.  
  4046. #ifdef    DOS
  4047.     if(*list_cntxt->context != '{'){    /* NOT an IMAP collection! */
  4048.     sprintf(searchname, "%4.4s*", folder_base);
  4049.     folder_base_len = strlen(searchname) - 1;
  4050.     }
  4051.     else
  4052. #endif
  4053.     sprintf(searchname, "%s*", folder_base);
  4054.  
  4055.     find_folders_in_context(NULL, list_cntxt, searchname);
  4056.     for(i = 0; i < folder_total(list_cntxt->folders); i++){
  4057.     filename = folder_entry(i, list_cntxt->folders)->name;
  4058. #ifdef    DOS
  4059.         if(struncmp(filename, folder_base, folder_base_len) == 0
  4060.            && strucmp(filename, folder_base)){
  4061.  
  4062.     if(*list_cntxt->context != '{'){
  4063.         int j;
  4064.         for(j = 0; j < 4; j++)
  4065.           if(!isdigit((unsigned char)filename[folder_base_len + j]))
  4066.         break;
  4067.  
  4068.        if(j < 4)        /* not proper date format! */
  4069.          continue;        /* keep trying */
  4070.     }
  4071. #else
  4072. #ifdef OS2
  4073.         if(strnicmp(filename, folder_base, folder_base_len) == 0
  4074.            && stricmp(filename, folder_base)){
  4075. #else
  4076.         if(strncmp(filename, folder_base, folder_base_len) == 0
  4077.            && strcmp(filename, folder_base)){
  4078. #endif
  4079. #endif
  4080.         sm->name = cpystr(filename);
  4081. #ifdef    DOS
  4082.         if(*list_cntxt->context != '{'){ /* NOT an IMAP collection! */
  4083.         sm->month_num  = (sm->name[folder_base_len] - '0') * 10;
  4084.         sm->month_num += sm->name[folder_base_len + 1] - '0';
  4085.         }
  4086.         else
  4087. #endif
  4088.             sm->month_num = month_num(sm->name + (size_t)folder_base_len + 1);
  4089.             sm++;
  4090.             if(sm >= &sml[MAX_FILES])
  4091.                break; /* Too many files, ignore the rest ; shouldn't occur */
  4092.         }
  4093.     }
  4094.  
  4095.     sm->name = cpystr("");
  4096.  
  4097.     /* anything to sort?? */
  4098.     if(sml->name && *(sml->name) && (sml+1)->name && *((sml+1)->name)){
  4099.     qsort(sml,
  4100.           sm - sml,
  4101.           sizeof(struct sm_folder),
  4102.           compare_sm_files);
  4103.     }
  4104.  
  4105.     return(sml);
  4106. }
  4107.  
  4108.  
  4109.  
  4110. /*----------------------------------------------------------------------
  4111.       Rename the current sent-mail folder to sent-mail for last month
  4112.  
  4113.    open up sent-mail and get date of very first message
  4114.    if date is last month rename and...
  4115.        if files from 3 months ago exist ask if they should be deleted and...
  4116.            if files from previous months and yes ask about them, too.   
  4117.   ----------------------------------------------------------------------*/
  4118. int
  4119. expire_sent_mail()
  4120. {
  4121.     int         cur_month, ok = 1;
  4122.     time_t     now;
  4123.     char     tmp[20], **p;
  4124.     struct tm    *tm_now;
  4125.     CONTEXT_S    *prune_cntxt;
  4126.  
  4127.     dprint(5, (debugfile, "==== expire_mail called ====\n"));
  4128.  
  4129.     now = time((time_t *)0);
  4130.     tm_now = localtime(&now);
  4131.  
  4132.     /*
  4133.      * If the last time we did this is blank (as if pine's run for
  4134.      * first time), don't go thru list asking, but just note it for 
  4135.      * the next time...
  4136.      */
  4137.     if(ps_global->VAR_LAST_TIME_PRUNE_QUESTION == NULL){
  4138.     ps_global->last_expire_year = tm_now->tm_year;
  4139.     ps_global->last_expire_month = tm_now->tm_mon;
  4140.     sprintf(tmp, "%d.%d", ps_global->last_expire_year,
  4141.         ps_global->last_expire_month + 1);
  4142.     set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 1);
  4143.     return(0);
  4144.     }
  4145.  
  4146.     if(ps_global->last_expire_year != -1 &&
  4147.       (tm_now->tm_year <  ps_global->last_expire_year ||
  4148.        (tm_now->tm_year == ps_global->last_expire_year &&
  4149.         tm_now->tm_mon <= ps_global->last_expire_month)))
  4150.       return(0); 
  4151.     
  4152.     cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
  4153.     dprint(5, (debugfile, "Current month %d\n", cur_month));
  4154.  
  4155.     /*
  4156.      * locate the default save context...
  4157.      */
  4158.     if(!(prune_cntxt = default_save_context(ps_global->context_list)))
  4159.       prune_cntxt = ps_global->context_list;
  4160.  
  4161.     /*
  4162.      * Since fcc's and read-mail can be an IMAP mailbox, be sure to only
  4163.      * try expiring a list if it's an ambiguous name associated with some
  4164.      * collection...
  4165.      *
  4166.      * If sentmail set outside a context, then pruning is up to the
  4167.      * user...
  4168.      */
  4169.     if(prune_cntxt){
  4170.     if(ps_global->VAR_DEFAULT_FCC && *ps_global->VAR_DEFAULT_FCC
  4171.        && context_isambig(ps_global->VAR_DEFAULT_FCC))
  4172.       ok = prune_folders(prune_cntxt, ps_global->VAR_DEFAULT_FCC,
  4173.                  cur_month, "SENT");
  4174.  
  4175.     if(ok && ps_global->VAR_READ_MESSAGE_FOLDER 
  4176.        && *ps_global->VAR_READ_MESSAGE_FOLDER
  4177.        && context_isambig(ps_global->VAR_READ_MESSAGE_FOLDER))
  4178.       ok = prune_folders(prune_cntxt, ps_global->VAR_READ_MESSAGE_FOLDER,
  4179.                  cur_month, "READ");
  4180.     }
  4181.  
  4182.     /*
  4183.      * Within the default prune context,
  4184.      * prune back the folders with the given name
  4185.      */
  4186.     if(ok && prune_cntxt && (p = ps_global->VAR_PRUNED_FOLDERS))
  4187.       for(; ok && *p; p++)
  4188.     if(**p && context_isambig(*p))
  4189.       ok = prune_folders(prune_cntxt, *p, cur_month, "");
  4190.  
  4191.     /*
  4192.      * Mark that we're done for this month...
  4193.      */
  4194.     if(ok){
  4195.     ps_global->last_expire_year = tm_now->tm_year;
  4196.     ps_global->last_expire_month = tm_now->tm_mon;
  4197.     sprintf(tmp, "%d.%d", ps_global->last_expire_year,
  4198.         ps_global->last_expire_month + 1);
  4199.     set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 1);
  4200.     }
  4201.  
  4202.     return(1);
  4203. }
  4204.  
  4205.  
  4206.  
  4207. /*----------------------------------------------------------------------
  4208.      Offer to delete old sent-mail folders
  4209.  
  4210.   Args: sml -- The list of sent-mail folders
  4211.  
  4212.   ----*/
  4213. int
  4214. prune_folders(prune_cntxt, folder_base, cur_month, type)
  4215.     CONTEXT_S *prune_cntxt;
  4216.     char      *folder_base, *type;
  4217.     int        cur_month;
  4218. {
  4219.     char         path[MAXPATH+1], path2[MAXPATH+1],  prompt[128], tmp[20];
  4220.     int          month_to_use, i;
  4221.     MAILSTREAM  *prune_stream;
  4222.     struct sm_folder *mail_list, *sm;
  4223.  
  4224.     mail_list = get_mail_list(prune_cntxt, folder_base);
  4225.  
  4226. #ifdef    DEBUG
  4227.     for(sm = mail_list; sm != NULL && sm->name[0] != '\0'; sm++)
  4228.       dprint(5, (debugfile,"Old sent-mail: %5d  %s\n",sm->month_num,sm->name));
  4229. #endif
  4230.  
  4231.     for(sm = mail_list; sm != NULL && sm->name[0] != '\0'; sm++)
  4232.       if(sm->month_num == cur_month - 1)
  4233.         break;  /* matched a month */
  4234.  
  4235.     month_to_use = (sm == NULL || sm->name[0] == '\0') ? cur_month - 1 : 0;
  4236.  
  4237.     dprint(5, (debugfile, "Month_to_use : %d\n", month_to_use));
  4238.  
  4239.     if(month_to_use == 0)
  4240.       goto delete_old;
  4241.  
  4242.     strcpy(path, folder_base);
  4243. /* BUG: how to check that a folder is zero_length via c-client?? */
  4244.     strcpy(path2, folder_base);
  4245.     strcpy(tmp, month_abbrev((month_to_use % 12)+1));
  4246.     lcase(tmp);
  4247. #ifdef    DOS
  4248.     if(*prune_cntxt->context != '{'){
  4249.       sprintf(path2 + (size_t)(((i = strlen(path2)) > 4) ? 4 : i),
  4250.           "%2.2d%2.2d", (month_to_use % 12) + 1,
  4251.           ((month_to_use / 12) - 1900) % 100);
  4252.     }
  4253.     else
  4254. #endif
  4255.     sprintf(path2 + strlen(path2), "-%s-%2d", tmp, month_to_use/12);
  4256.  
  4257.     Writechar(BELL, 0);
  4258.     sprintf(prompt, "Move current \"%s\" to \"%s\"", path, path2);
  4259.     switch(folder_exists(prune_cntxt->context, folder_base)){
  4260.       case -1 :            /* error! */
  4261.         dprint(5, (debugfile, "prune_folders: Error testing existance\n"));
  4262.         return(0);
  4263.  
  4264.       case 0 :            /* doesn't exist */
  4265.         dprint(5, (debugfile, "prune_folders: nothing to prune <%s %s>\n",
  4266.            prune_cntxt->context, folder_base));
  4267.         goto delete_old;
  4268.  
  4269.       default :
  4270.     if(want_to(prompt, 'n', 0, h_wt_expire, 1, 1) == 'n'){
  4271.         dprint(5, (debugfile, "User declines renaming %s\n",
  4272.                ps_global->VAR_DEFAULT_FCC));
  4273.         goto delete_old;
  4274.     }
  4275.     /* else fall thru processing matching folders */
  4276.  
  4277.     break;
  4278.     }
  4279.  
  4280.     /*--- User says OK to rename ---*/
  4281.     dprint(5, (debugfile, "rename \"%s\" to \"%s\"\n", path, path2));
  4282.     prune_stream = context_same_stream(prune_cntxt->context, path2,
  4283.                        ps_global->mail_stream);
  4284.  
  4285.     if(!prune_stream && ps_global->mail_stream != ps_global->inbox_stream)
  4286.       prune_stream = context_same_stream(prune_cntxt->context, path2,
  4287.                      ps_global->inbox_stream);
  4288.  
  4289.     if(!context_rename(prune_cntxt->context, prune_stream, path, path2)){
  4290.         q_status_message2(SM_ORDER | SM_DING, 3, 4,
  4291.               "Error renaming \"%s\": %s",
  4292.                           pretty_fn(folder_base),
  4293.               error_description(errno));
  4294.         dprint(1, (debugfile, "Error renaming %s to %s: %s\n",
  4295.                    path, path2, error_description(errno)));
  4296.         display_message('x');
  4297.         goto delete_old;
  4298.     }
  4299.  
  4300.     context_create(prune_cntxt->context, prune_stream ? prune_stream : NULL,
  4301.            folder_base);
  4302.  
  4303.   delete_old:
  4304.     delete_old_mail(mail_list, prune_cntxt, type);
  4305.     if(sm = mail_list){
  4306.     while(sm->name){
  4307.         fs_give((void **)&(sm->name));
  4308.         sm++;
  4309.     }
  4310.  
  4311.         fs_give((void **)&mail_list);
  4312.     }
  4313.  
  4314.     return(1);
  4315. }
  4316.  
  4317.  
  4318. /*----------------------------------------------------------------------
  4319.      Offer to delete old sent-mail folders
  4320.  
  4321.   Args: sml       -- The list of sent-mail folders
  4322.         fcc_cntxt -- context to delete list of folders in
  4323.         type      -- label indicating type of folders being deleted
  4324.  
  4325.   ----*/
  4326. void
  4327. delete_old_mail(sml, fcc_cntxt, type)
  4328.     struct sm_folder *sml;
  4329.     CONTEXT_S        *fcc_cntxt;
  4330.     char             *type;
  4331. {
  4332.     char  prompt[150];
  4333.     int   rc;
  4334.     struct sm_folder *sm;
  4335.     MAILSTREAM       *del_stream;
  4336.  
  4337.     for(sm = sml; sm != NULL && sm->name[0] != '\0'; sm++){
  4338.         sprintf(prompt,
  4339.            "To save disk space, delete old %.4s mail folder \"%.30s\" ",
  4340.            type, sm->name);
  4341.         if(want_to(prompt, 'n', 0, h_wt_delete_old, 1, 1) == 'y'){
  4342.         del_stream = context_same_stream(fcc_cntxt->context, sm->name,
  4343.                          ps_global->mail_stream);
  4344.  
  4345.         if(!del_stream 
  4346.            && ps_global->mail_stream != ps_global->inbox_stream)
  4347.           del_stream = context_same_stream(fcc_cntxt->context, sm->name,
  4348.                            ps_global->inbox_stream);
  4349.  
  4350.         if(!context_delete(fcc_cntxt->context, del_stream, sm->name)){
  4351.         q_status_message1(SM_ORDER,
  4352.                   3, 3, "Error deleting \"%s\".", sm->name);
  4353.         dprint(1, (debugfile, "Error context_deleting %s in \n",
  4354.                sm->name, fcc_cntxt->context));
  4355.             }
  4356.     }else{
  4357.         /* break; /* skip out of the whole thing when he says no */
  4358.         /* Decided to keep asking anyway */
  4359.         }
  4360.     }
  4361. }
  4362.